Foreword
RSS难得新增一员,体验一下Follow,看看和TTRss有啥不一样的,有啥改进的点
Follow
https://follow.is/
Follow支持opml导入,所以TTRSS的内容可以直接导进来,对应也有配套的APP,不用自己去找支持TTRSS的订阅APP,生态整体比较完整
Follow整个界面也更加现代一些,TTRSS则是更简洁,更快,动效上明显多了很多内容
相比TTRSS可能需要设置各种插件来爬取文章内容,Follow似乎把这一块都简化了,更容易上手一些
打开某一个blog的文章,是可以看到有多少人也一起订阅了这个网站的内容
甚至可以看到我自己的的网站被多少人订阅了,我可以看到具体谁阅读过我的文章
认证
如果这是你的订阅源,需要本人认证,可以通过下面的三种方式
大致看了下,内容和描述都要插入一些奇怪的东西,不喜欢,还是插入RSS标签好一点
对应我Blog中的feed.xml增加标签即可
---
layout: null
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Elmagnifico's Blog</title>
<description>后端与嵌入式、maya与游戏 | elmagnifico, Back-end engineer & Embedded System Lover | 这里是elmagnifico的个人博客,君子之交淡如水。</description>
<link>https://elmagnifico.tech/</link>
<atom:link href="https://elmagnifico.tech/feed.xml" rel="self" type="application/rss+xml" />
<pubDate>Wed, 22 Jan 2025 17:53:04 +0800</pubDate>
<lastBuildDate>Wed, 22 Jan 2025 17:53:04 +0800</lastBuildDate>
<generator>Jekyll v4.3.2</generator>
<follow_challenge>
<feedId>55855418052542483</feedId>
<userId>101522814280570880</userId>
</follow_challenge>
<item>
<title>2024游戏短评</title>
<description>## Foreword
一年一度短评环节又到了
steam自建的2024年游戏回顾
> https://store.steampowered.com/replay/76561198094167163/2024
## 今年游玩
今年一共玩了44款游戏,比去年还多一些,60%都是新游戏
#### 大侠立志传
![image-20240210202138020](https://img.elmagnifico.tech/static/upload/elmagnifico/image-20240210202138020.png)
大侠立志传确实是又一个金庸群侠传,很不错
神仙醋确实继承了以前金庸群侠传的衣钵,今年还看到了神仙醋他们的的纪录片,想做一个这样的小游戏确实不容易,醋神他们也是暴死了好几次,靠着一款产品续命,才能养活这样一个单机游戏,而单机游戏想要长期收入是不太可能的,做完一款就必须投入到下一款去了。对于制作人来说,相当于是一直在绞尽脑汁挖掘灵感,制作游戏,否则就会陷入没有产出,收入下滑的问题。
对于武侠游戏、开放世界、像素风,这个类型来说,盘子确实比较小,能带来的收入是有限的,而且最重要的是他有很大可能会暴死,只要玩家不买账,那么这一年或者两年的投入直接打水漂,对于团队来说这种风险很高,得要有一帮子多年的老人才能玩的下去。
对于神仙醋熟悉的领域来说,会买账的大多也是当年的一些人了,更年轻的人他们想要的东西,有些时候其实和老人的审美或者角度,已经不一样了。想要再成功一次可能是比较难的,而对于一些经典模式的复刻反而是比较稳妥的实现。
#### Super Raft Boat Together
![image-20250122154654538](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221547706.png)
游戏内容过少了,难度相对也比较简单,定价58,实在是有点吃亏,8块可能比较合适
#### 熔炉密林
![image-20250122154854504](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221548641.png)
手残的一年,但是把熔炉密林打通了,新DLC也随便打通了。游戏看起来简单,但是实际操作有很多技巧,这些游戏内都不是很容易摸索出来,需要外部攻略,游戏又非常肝,需要反复刷很久才能遇到想要的装备。
#### 土豆兄弟
![image-20250122155108327](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221551456.png)
从家庭组白嫖的游戏,也挺好玩的,自由组合搭配各种build,节奏比吸血鬼幸运者快一些,能玩的组合很多种
#### 暖雪
![image-20250122155223543](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221552679.png)
前几年出的Roguelite游戏,今年才有空白嫖玩一下,操作很爽,自己摸到了一个套路,直接全通关了。游戏内build也蛮丰富的,我估计就玩了10%不到的可能性
#### 传说法师1/2Demo
![image-20250122155600570](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221556686.png)
传说法师重温了1代,确实挺难的,找了几个组合还是有点手残不好打,打击感超棒的。
传说法师2代也同时发布了,不过2代有点不好玩,Demo流程很短,很多东西都没解锁,手残玩起来很难。
后续2代发布以后,直接就褒贬不一了
#### Hades
![image-20250122155428853](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221554973.png)
家庭分享,哈迪斯整体操作手感是真的不错,对比暖雪、熔炉、传说法师等等,画面细节、北欧众神、各种搞笑的小桥段,整个游戏刷是一方面,但是剧情在循序渐进,让你有一个明显的盼头,知道下一次刷他会出现什么东西。 一代基本都是近战或者类似近战的操作,非常耐玩,手残也能打过最终的boss。
#### Hades2
![image-20250122160021461](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221600589.png)
哈迪斯2就有点难评了,2代以妹妹为操作对象,妹妹是个法师,很多操作都改变了,2代需要很多长按,蓄力类的操作来打伤害,和一代的那种按一下就能打出来伤害来说,操作显得极其难受。2代的进阶操作或者说更高伤害都需要长按,不知道为什么要这么设计,这样手感就变得很差,虽然打通了,但是总是爽不起来。
由于2代是EA,所以很多内容都还没完善,有点意思的是2代在游戏中可以直接进入1代场景,也顺便看了一下2代的游戏代码,竟然就是lua直接控制的,那其实1代就给出来一个游戏框架了,2代只需要填入素材和数值,就可以直接套模板,所以一二代可以直接实现穿越式的体验,甚至很多Mod或者修改,都是直接修改源码中的条件,如果有人愿意再深入一下,完全可以以Hades为基础,搞一个自己的游戏。
#### Darkest Dungeon
![image-20250122160558786](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221605944.png)
之前先玩过了铁轨与墓穴,EA的时候很简单,说是和这个游戏差不多,所以来看看,再看到这个游戏这么多年了还有人在玩,在直播,不可思议,所以试试。一试发现这游戏太难了,打了N个mod,简化了游戏内容,我都觉得难得要死,不会再玩第二次了。暗黑地牢,是真的黑暗,一个人玩都能玩的心惊胆战,团灭了三次,每次都想弃坑,好不容易培养的4个6级顶级天赋的人直接变坟堆,气得要死。
最后开着加速器总算是把游戏剧情全打通了,这还是最简单的难度,最高难度不敢想象,一个个都是数值管理大师。
#### 火山的女儿
![image-20250122161230253](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221612450.png)
额,一个养女儿的游戏,不是这类游戏受众,只能说来体验一下为什么这么多人玩这个,感觉算是以前的4399集大成者,缝合了很多内容进来,但是比较巧妙的是作者没有让每个东都变得很难受,控制了一下复杂度,大多数情况都可以稍微思考一下找到解,没有让系统爆炸,没有那么紧迫的感觉,慢慢养女儿就行。只能说还可以,但是我不喜欢。
#### 刀剑江湖路
![image-20250122161703034](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221617214.png)
看起来是想做一个武侠+DNF+金庸群侠传的游戏,甚至可能还有一点太吾的意思,不过系统不完善,目前剧情很多也没做完,分支也没有,主角很多时候的选择完全没用。虽然是DNF式的横板战斗搓招,但是手感差的要死,操控起来很难受。
如果有老菊之类的再给他做个长视频,游戏机制再完善完善,估计会很有意思
#### 超级键盘侠
![img](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221620892.jpeg)
一个挺恶搞的游戏,游戏里梗比较多,但是套路有点少
#### Glyphica Typing Survival
![image-20250122162311858](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221623933.png)
> https://elmagnifico.tech/2024/05/30/Glyphica/
一个打字游戏,非常有意思,之前评论过了不再重复,作者也在加班加点搞中文版本,很不错
#### 夜族崛起
![image-20250122162512018](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221625178.png)
化身吸血鬼,培养血奴,害怕太阳,把吸血鬼复刻到游戏里,整体还能联机、生存、剧情、刷刷刷、还需要一些操作,有build可玩,就是后期肝度实在是太高了,游戏里的极品道具都非常难获取,估计上百小时才能刷满一套。游戏完成度比较高,还在持续更新。
#### Abiotic Factor
![image-20250122163000517](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221630713.png)
非生物因素,把恐怖元素结合进来的生存游戏,多人联机会比较好,游戏内容还不完善,剧情也没有写完,还是等后续更新吧
#### 小丑牌
![image-20250122163408774](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221634959.png)
策略游戏,有意思,一出就风靡全球,简单的玩法,一上手就都懂了,一局时间相对比较短,由此开启一个新游戏模式,各种丑牌、甚至麻将等等模仿者接接踵而至。
#### Chained together
![image-20250122163646639](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221636830.png)
平台跳跃游戏,但是能联机,这种游戏以前也有很多,但是这个游戏他把人链接在一起了,游戏中间可以有保存点,错了还能重来,疯狂压力队友,贼搞笑,不过游戏目前场景比较少,如果后续有自定义工坊等等,内容更多了,可能比较欢乐。
#### 戴森球计划
![image-20250122165149276](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221651469.png)
一直想玩一直拖着,拖到这游戏都4年了,很像程序员开发的东西,整个流水线的配置和写代码式的,不过游戏目标不知道是什么,没有动力玩下去了
#### 杀戮台球
![image-20250122165759497](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221657682.png)
杀戮台球,把策略和各种build带到了台球里,也不是很贵,玩起来比较轻松
#### 中华一商
![image-20250122170412252](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221704378.png)
免费领的游戏,就是过程有点繁琐,中间偏后期很无聊
#### ASTRONEER
![image-20250122170711615](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221707802.png)
当年玩的时候游戏内容还很少,后面越发展内容越多了,后期的玩法就是3D版本的戴森球了,自动化工厂流水线,每年大概有几个活动事件,总体也是挂机自动化的干活
#### POE2
![image-20250122170941291](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221709470.png)
poe的二代,当年就听说开发了,现在才出来,二代相对一代去掉了系统里很多很繁杂的设定,整个系统变得简单了。
玩了一个月差不多终局毕业了,就是这个游戏排骨人太多了,基本全都是来刷了赚钱的,没有好好玩游戏的,有点难受
#### 雨中冒险:回归
![image-20250122171632696](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221716790.png)
雨冒一代,操作有点难受,反人类,不知道为啥重置不愿意改一下,整体玩法一般般,还是二代好玩
#### 失落城堡2
![img](https://shared.cloudflare.steamstatic.com/store_item_assets/steam/apps/2445690/header.jpg?t=1737007965)
之前失落城堡各种父子玩耍,现在总算出二代了,玩了Demo,但是bug一堆,玩法深度也一般,等到EA版本出来,果然直接跌下神坛,多半好评了。
## 已买待玩
#### 雾锁王国
![image-20250122171401542](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221714727.png)
这个去年就要玩来着,还是没玩,据说是类似夜族崛起的那种模式,不过还在EA,还能等正式版
#### 绝世好武功
![image-20250122173111981](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221731157.png)
1.0版本总算出了,可以重新体验一下了
#### 大江湖之苍龙与白鸟
![image-20250122172524627](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221725815.png)
这游戏跳票这么久,大侠同时期的游戏,现在都还没做完,等他实际完成看看吧
#### 博德之门3
![image-20250122172705184](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221727367.png)
今年可能会试试这个游戏,看看他是什么样的模式,为什么这么多好评
#### 赛博朋克
![image-20250122173511688](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221735868.png)
可能会玩一下
#### 巫师3
![image-20250122173551196](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221735340.png)
可能会玩一下
#### 小缇娜的奇幻之地
![image-20250122173621321](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221736482.png)
无主的番外篇,据说无主4也要出了,可以体验一下
#### 诺兰德
![image-20250122173744967](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221737143.png)
一直没空玩,在等他EA结束,似乎现在可以了
## 新游期待
主要是一些Demo游戏的游玩体验,感觉不错
#### 咒印链接
![image-20250122163950327](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221639419.png)
即时战斗策略游戏,游戏里可以得到各种各样的链接或者道具,连在一起就可以形成各种comb,最终通关。有点以前POE的宝石链接的意思,但是可以连得更长,更多种形式,目前Demo很不错,期待后期发售
#### 腐朽默示
![img](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221643246.jpeg)
一个像素风,但是可以生存、建造、打僵尸、招小弟的游戏,可惜只能单人,目前玩起来还可以。
如果能支持多人联机估计会更好
#### 息风谷战略
![img](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221646886.jpeg)
策略游戏,但是回合战斗,同时又能自动战斗,还能养成,养老婆的游戏,Demo的可玩内容已经挺多的了,不过玩法也比较单一,目前深度不是很够,build玩法没有明显构建起来
#### Sephiria
![image-20250122164847406](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221648514.png)
像素风的Roguelite游戏,游戏难度比较高,也是背包管理类型,格子各种搭配可以组合出来一些特效,手感也不错。
#### Hell Clock
![image-20250122172904062](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221729223.png)
玩的时间不长,但是作为Rougelite+刷子游戏,总体build还是有点意思的
#### Monster Hunter Wilds
![image-20250122165921390](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221659572.png)
怪猎荒野,试玩,但是感觉机器带不动了,卡卡的,任务引导也莫名其妙的,手感似乎和上一代差不多,等2.28发售再说吧
#### 梦之形
![image-20250122170240448](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221702625.png)
Roguelite游戏,MOBA风格,刷起来很爽,支持联机,已经打通全程,今年在日本还在展会遇到了制作组,挺不错的
## 其他
#### 活侠传
没玩,但是出了果然就挨骂了,剧本不错,但是评论两极分化
#### 黑神话:悟空
![image-20250122174023146](https://img.elmagnifico.tech/static/upload/elmagnifico/202501221740432.png)
云完了,没有玩,挺不错的,不过不是我喜欢的类型
## Summary
还是有一些期待2025的
</description>
<pubDate>Wed, 22 Jan 2025 00:00:00 +0800</pubDate>
<link>https://elmagnifico.tech/2025/01/22/Game-Comment5/</link>
<guid isPermaLink="true">https://elmagnifico.tech/2025/01/22/Game-Comment5/</guid>
<category>Game</category>
</item>
<item>
<title>SES Threads 自定义JS脚本</title>
<description>## Foreword
最近又遇到了一些奇怪的bug,意外发现调试手段上还是有所欠缺,而SES的js脚本可以做更多自定义的工具,可以进一步改造方便疑难杂症使用
## Threads Script
需要注意Threads Script用的是Java Script,而不是Jlink Script(前面查了半天发现不对),虽然都简称成了js,但是Jlink Script应该只有Jlink的一些批处理程序才会使用这种方式写脚本
之前有说过SES的RTOS Awareness好像有点问题,老是显示不全,不如Ozone的
![image-20250121152552349](https://img.elmagnifico.tech/static/upload/elmagnifico/20250121152552469.png)
貌似这个问题在我提给官方以后修复了
![image-20250121152724232](https://img.elmagnifico.tech/static/upload/elmagnifico/20250121152724290.png)
之前有提到显示操作系统线程情况的脚本,这里发现其实这个东西可以自定义的程度还是蛮高的
```js
/*********************************************************************
* SEGGER MICROCONTROLLER GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1995 - 2019 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* Please note: *
* *
* Knowledge of this file may under no circumstances *
* be used to write a similar product *
* *
* Thank you for your fairness ! *
* *
**********************************************************************
* *
* Current version number will be inserted here *
* when shipment is built. *
* *
**********************************************************************
----------------------------------------------------------------------
File : FreeRTOSPlugin_CM7.js
Purpose : Ozone FreeRTOS-awareness JavaScript Plugin for Cortex-M7
---------------------------END-OF-HEADER------------------------------
*/
/*********************************************************************
*
* Local Functions
*
**********************************************************************
*/
/*********************************************************************
*
* GetTaskStackGrowDir
*
* Function description
* Indicates if the task stack grows in positive (1) or negative (-1) memory direction
*
* Notes
* (1) The stack grow direction depends on the FreeRTOS-port.
* Currently, a positive grow direction is only used on the PIC architecture.
*/
function GetTaskStackGrowDir() {
return -1;
}
/*********************************************************************
*
* GetTaskPrioName
*
* Function description
* Returns the display text of a task priority
*
* Parameters
* Priority: task priority (integer)
*/
function GetTaskPrioName(Priority) {
var sName;
if (Priority == 0) {
sName = "Idle"
} else if (Priority == 1) {
sName = "Low"
} else if (Priority == 2) {
sName = "Normal";
} else if (Priority == 3) {
sName = "High";
} else if (Priority == 4) {
sName = "Highest";
} else {
return Priority.toString();
}
sName = sName + " (" + Priority + ")";
return sName;
}
/*********************************************************************
*
* GetTaskName
*
* Function description
* Returns the display text of a task name
*
* Parameters
* tcb: task control block (type tskTCB)
* Addr: control block memory location
*/
function GetTaskName(tcb, Addr) {
var sTaskName;
sTaskName = Debug.evaluate("(char*)(*(tskTCB*)" + Addr + ").pcTaskName");
if (sTaskName == undefined) {
sTaskName = Debug.evaluate("(char*)(*(TCB_t*)" + Addr + ").pcTaskName");
}
if (tcb.uxTCBNumber != undefined) {
sTaskName = "#" + tcb.uxTCBNumber + " \"" + sTaskName + "\"";
}
return sTaskName;
}
/*********************************************************************
*
* GetTaskID
*
* Function description
* Returns the display text of a task ID
*
* Parameters
* tcb: task control block (type tskTCB)
* Addr: control block memory location
*/
function GetTaskID(tcb, Addr) {
return "0x" + (tcb.uxTCBNumber == undefined ? Addr.toString(16) : tcb.uxTCBNumber.toString(16));
}
/*********************************************************************
*
* GetTCB
*
* Function description
* Returns the task control block of a task
*
* Parameters
* Addr: control block memory location
*/
function GetTCB(Addr) {
var tcb;
tcb = Debug.evaluate("*(tskTCB*)" + Addr);
if (tcb == undefined) {
tcb = Debug.evaluate("*(TCB_t*)" + Addr);
}
return tcb;
}
/*********************************************************************
*
* GetTaskNotifyStateStr
*
* Function description
* Returns the display string of a task notify state
*
* Parameters
* tcb: task control block (type tskTCB)
*/
function GetTaskNotifyStateStr(tcb) {
if (tcb.ucNotifyState == undefined) {
return tcb.eNotifyState == undefined ? "N/A" : tcb.eNotifyState.toString();
} else {
return tcb.ucNotifyState.toString()
}
}
/*********************************************************************
*
* GetTaskStackInfoStr
*
* Function description
* Returns a display text of the format "<free space> / <stack size>"
*
* Parameters
* tcb: task control block (type tskTCB)
*
* Notes
* (1) pxEndOfStack is only available when FreeRTOS was compiled with
* configRECORD_STACK_HIGH_ADDRESS == 1
*/
function GetTaskStackInfoStr(tcb) {
var FreeSpace;
var UsedSpace;
var Size;
// GrowDir == 0 GrowDir == 1
//
// pxStack | Low Addr | Stack Top | Stack Base |
// | | | |
// pxTopOfStack | | Stack Pointer | Stack Pointer |
// | | | |
// pxEndOfStack | High Addr | Stack Base | Stack Top |
//
if (GetTaskStackGrowDir() == 1) { // stack grows in positive address direction
if (tcb.pxEndOfStack == undefined) {
UsedSpace = tcb.pxTopOfStack - tcb.pxStack;
FreeSpace = undefined;
Size = undefined;
} else {
FreeSpace = tcb.pxEndOfStack - tcb.pxTopOfStack;
UsedSpace = tcb.pxTopOfStack - tcb.pxStack;
Size = FreeSpace + UsedSpace;
}
} else { // stack grows in negative address direction
if (tcb.pxEndOfStack == undefined) {
FreeSpace = tcb.pxTopOfStack - tcb.pxStack;
UsedSpace = undefined;
Size = undefined;
} else {
FreeSpace = tcb.pxTopOfStack - tcb.pxStack;
UsedSpace = tcb.pxEndOfStack - tcb.pxTopOfStack;
Size = FreeSpace + UsedSpace;
}
}
return FreeSpace + " / " + (Size == undefined ? "N/A" : Size);
}
function GetLabel1(tcb) {
var FreeSpace;
var UsedSpace;
var Size;
// GrowDir == 0 GrowDir == 1
//
// pxStack | Low Addr | Stack Top | Stack Base |
// | | | |
// pxTopOfStack | | Stack Pointer | Stack Pointer |
// | | | |
// pxEndOfStack | High Addr | Stack Base | Stack Top |
//
if (GetTaskStackGrowDir() == 1) { // stack grows in positive address direction
if (tcb.pxEndOfStack == undefined) {
UsedSpace = tcb.pxTopOfStack - tcb.pxStack;
FreeSpace = undefined;
Size = undefined;
} else {
FreeSpace = tcb.pxEndOfStack - tcb.pxTopOfStack;
UsedSpace = tcb.pxTopOfStack - tcb.pxStack;
Size = FreeSpace + UsedSpace;
}
} else { // stack grows in negative address direction
// FreeRTOS -1 ,go on here
if (tcb.pxEndOfStack == undefined) {
FreeSpace = tcb.pxTopOfStack - tcb.pxStack;
UsedSpace = undefined;
Size = undefined;
} else {
FreeSpace = tcb.pxTopOfStack - tcb.pxStack;
UsedSpace = tcb.pxEndOfStack - tcb.pxTopOfStack;
Size = FreeSpace + UsedSpace;
}
}
return "0x"+tcb.pxTopOfStack.toString(16) + " / " + "0x"+tcb.pxStack.toString(16);
}
function GetLabel2(addr) {
return addr.toString(16);
}
function GetLabel3(param) {
return param.toString(16);
}
/*********************************************************************
*
* AddTask
*
* Function description
* Adds a task to the task window
*
* Parameters
* Addr: memory location of the task's control block (TCB)
* CurrTaskAddr: memory location of the executing task's control block (TCB)
* sState: state of the task (e.g. "executing")
*/
function AddTask(Addr, CurrTaskAddr, sState) {
var tcb;
var sTaskName;
var sPriority;
var sRunCnt;
var sMutexCnt;
var sTimeout;
var sStackInfo;
var sID;
var sNotifiedValue;
var sNotifyState;
var Lable1
var Lable2
var Lable3
tcb = GetTCB(Addr);
sTaskName = GetTaskName(tcb, Addr);
sID = GetTaskID(tcb, Addr);
sStackInfo = GetTaskStackInfoStr(tcb);
sPriority = GetTaskPrioName(tcb.uxPriority);
sNotifyState = GetTaskNotifyStateStr(tcb);
sTimeout = (tcb.Timeout == undefined ? "N/A" : tcb.Timeout.toString());
sRunCnt = (tcb.ulRunTimeCounter == undefined ? "N/A" : tcb.ulRunTimeCounter.toString());
sNotifiedValue = (tcb.ulNotifiedValue == undefined ? "N/A" : tcb.ulNotifiedValue.toString());
sMutexCnt = (tcb.uxMutexesHeld == undefined ? "N/A" : tcb.uxMutexesHeld.toString());
// custom labels
Lable1 = GetLabel1(tcb);
Lable2 = GetLabel2(Addr);
Lable3 = GetLabel3(0);
if (Addr == CurrTaskAddr) {
sState = "executing";
}
Threads.add(sTaskName, sRunCnt, sPriority, sState, sTimeout, sStackInfo, sID, sMutexCnt, sNotifiedValue, sNotifyState, Lable1,Lable2,Lable3,(Addr == CurrTaskAddr) ? undefined : Addr);
}
/*********************************************************************
*
* AddList
*
* Function description
* Adds all tasks of a task list to the task window
*
* Parameters
* List: task list (type xLIST)
* CurrTaskAddr: memory location of the executing task's control block (TCB)
* sState: common state of all tasks within 'list'
*/
function AddList(List, CurrTaskAddr, sState) {
var i;
var Index;
var Item;
var TaskAddr;
if ((List != undefined) && (List.uxNumberOfItems > 0)) {
Index = List.xListEnd.pxNext;
for (i = 0; i < List.uxNumberOfItems; i++) {
Item = Debug.evaluate("*(xLIST_ITEM*)" + Index);
TaskAddr = Item != 0 ? Item.pvOwner : 0;
if (TaskAddr != 0) {
AddTask(TaskAddr, CurrTaskAddr, sState);
}
Index = Item.pxNext;
if (i > 1000) { // infinite loop guard
break;
}
}
}
}
/*********************************************************************
*
* API Functions
*
**********************************************************************
*/
/*********************************************************************
*
* init
*
* Function description
* Initializes the task window
*/
function init() {
Threads.clear();
Threads.newqueue("Task List");
Threads.setColumns("Name", "Run Count", "Priority", "Status", "Timeout", "Stack Info", "ID", "Mutex Count", "Notified Value", "Notify State","Lable1","Lable2","Lable3");
Threads.setColor("Status", "ready", "executing", "blocked");
}
/*********************************************************************
*
* update
*
* Function description
* Updates the task window
*/
function update() {
var i;
var pList;
var List;
var MaxPriority;
var CurrTaskAddr;
Threads.clear();
if((Debug.evaluate("pxCurrentTCB") == 0) || (Debug.evaluate("pxCurrentTCB") == undefined)) {
return;
}
MaxPriority = Debug.evaluate("uxTopReadyPriority");
CurrTaskAddr = Debug.evaluate("pxCurrentTCB");
for (i = MaxPriority; i >= 0; i--) {
List = Debug.evaluate("pxReadyTasksLists[" + i + "]");
AddList(List, CurrTaskAddr, "ready");
}
pList = Debug.evaluate("pxDelayedTaskList");
if (pList != 0) {
List = Debug.evaluate("*(xLIST*)" + pList);
AddList(List, CurrTaskAddr, "blocked");
}
pList = Debug.evaluate("pxOverflowDelayedTaskList");
if (pList != 0) {
List = Debug.evaluate("*(xLIST*)" + pList);
AddList(List, CurrTaskAddr, "blocked");
}
List = Debug.evaluate("xSuspendedTaskList");
if (List != 0) {
AddList(List, CurrTaskAddr, "suspended");
}
}
/*********************************************************************
*
* getregs
*
* Function description
* Returns the register set of a task.
* For ARM cores, this function is expected to return the values
* of registers R0 to R15 and PSR.
*
* Parameters
* hTask: integer number identifiying the task.
* Identical to the last parameter supplied to method Threads.add.
* For convenience, this should be the address of the TCB.
*
* Return Values
* An array of unsigned integers containing the tasks register values.
* The array must be sorted according to the logical indexes of the regs.
* The logical register indexing scheme is defined by the ELF-DWARF ABI.
*
**********************************************************************
*/
function getregs(hTask) {
var i;
var SP;
var LR;
var Addr;
var tcb;
var aRegs = new Array(17);
tcb = GetTCB(hTask);
SP = tcb.pxTopOfStack;
Addr = SP;
/* the following registers are pushed by the FreeRTOS-scheduler */
//
// R4...R11
//
for (i = 4; i < 12; i++) {
aRegs[i] = TargetInterface.peekWord(Addr);
Addr += 4;
}
//
// EXEC_RET
//
LR = TargetInterface.peekWord(Addr);
Addr += 4;
//
// S16...S31
//
if ((LR & 0x10) != 0x10) { // FP context has been saved?
Addr += 4*16; // skip S16..S31
}
/* the following registers are pushed by the ARM core */
//
// R0...R3
//
for (i = 0; i < 4; i++) {
aRegs[i] = TargetInterface.peekWord(Addr);
Addr += 4;
}
//
// R12, LR, PC, PSR
//
aRegs[12] = TargetInterface.peekWord(Addr);
Addr += 4;
aRegs[14] = TargetInterface.peekWord(Addr);
Addr += 4;
aRegs[15] = TargetInterface.peekWord(Addr);
Addr += 4;
aRegs[16] = TargetInterface.peekWord(Addr);
Addr += 4;
//
// S0..S15
//
if ((LR & 0x10) != 0x10) { // FP context has been saved?
Addr += 4*18; // skip S0...S15
}
if (aRegs[16] & (1<<9)) { // Stack has been 8-byte aligned
Addr += 4;
}
//
// SP
//
aRegs[13] = Addr;
return aRegs;
}
/*********************************************************************
*
* getContextSwitchAddrs
*
* Functions description
* Returns an unsigned integer array containing the base addresses
* of all functions that complete a task switch when executed.
*/
function getContextSwitchAddrs() {
var aAddrs;
var Addr;
Addr = Debug.evaluate("&vTaskSwitchContext");
if (Addr != undefined) {
aAddrs = new Array(1);
aAddrs[0] = Addr;
return aAddrs;
}
return [];
}
/*********************************************************************
*
* getOSName()
*
* Functions description:
* Returns the name of the RTOS this script supplies support for
*/
function getOSName() {
return "FreeRTOS";
}
这里主要是增加了三个标签的显示
可以显示栈的起始指针和当前指针位置、TCB指针位置,还多了一个标签待定,随便定义显示什么
整个脚本里主要显示的表格是Threads,只要在它里面去加内容就行了
function init() {
Threads.clear();
Threads.newqueue("Task List");
Threads.setColumns("Name", "Run Count", "Priority", "Status", "Timeout", "Stack Info", "ID", "Mutex Count", "Notified Value", "Notify State","Lable1","Lable2","Lable3");
Threads.setColor("Status", "ready", "executing", "blocked");
}
剩下显示具体变量也好,还是什么其他东西也好,都可以使用这个函数来完成
Debug.evaluate("pxCurrentTCB")
他会把对应的变量转成实际的内容返回
如果要调试某个函数,还可以通过这种方式来获取到具体的函数名称
Debug.getfunction(address)
如果相加额外的表格列,对应加上一个数字即可
function init()
{
...
Threads.setColumns2("Timers", "Id(Timers)", "Name", "Hook", "Timeout", "Period", "Active");
function update()
{
...
Threads.add2("Timers", "0x1FF0A30", "MyTimer", "0x46C8 (Timer50)", "50(550)", "50", "1");
每次更新脚本以后只需要Reload一下,就可以直接加载允许了,不需要断开jlink重连什么的,还是非常方便的
特别是如果遇到踩内存或者需要遍历各个队列什么的,就可以用js脚本直接写一个然后输出到外部框里就行了
其他
虽然官方文档里有Echo,类似console.log,但是试了一下在js这里写这个内容是完全无效的,可以正常执行,但是结果不知道输出到哪里去了
WScript.Echo(s)
这个小插件的报错有点不友好,很容易看不出来具体错误是什么,要怎么改
而又缺少对这个小插件调试的手段,只能一遍遍试着跑,有点蛋疼
Summary
好用的脚本还是得多积累积累,下次遇到问题才能派上用场
Quote
> https://studio.segger.com/index.htm?https://studio.segger.com/ide_debug_expressions.htm
</description>
<category>SES</category>
</item>
<item>
<title>RSS Follow体验</title>
<description>## Foreword
RSS难得新增一员,体验一下Follow,看看和TTRss有啥不一样的,有啥改进的点
Follow
> https://follow.is/
Follow支持opml导入,所以TTRSS的内容可以直接导进来,对应也有配套的APP,不用自己去找支持TTRSS的订阅APP,生态整体比较完整
Follow整个界面也更加现代一些,TTRSS则是更简洁,更快,动效上明显多了很多内容
相比TTRSS可能需要设置各种插件来爬取文章内容,Follow似乎把这一块都简化了,更容易上手一些
打开某一个blog的文章,是可以看到有多少人也一起订阅了这个网站的内容
甚至可以看到我自己的的网站被多少人订阅了,我可以看到具体谁阅读过我的文章
认证
如果这是你的订阅源,需要本人认证,可以通过下面的三种方式
大致看了下,内容和描述都要插入一些奇怪的东西,不喜欢,还是插入RSS标签好一点
对应我Blog中的feed.xml增加标签即可
---
layout: null
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{ site.title | xml_escape }}</title>
<description>{{ site.description | xml_escape }}</description>
<link>{{ site.url }}{{ site.baseurl }}/</link>
<atom:link href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" rel="self" type="application/rss+xml" />
<pubDate>{{ site.time | date_to_rfc822 }}</pubDate>
<lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
<generator>Jekyll v{{ jekyll.version }}</generator>
<follow_challenge>
<feedId>55855418052542483</feedId>
<userId>101522814280570880</userId>
</follow_challenge>
{% for post in site.posts limit:10 %}
<item>
<title>{{ post.title | xml_escape }}</title>
<description>{{ post.content | xml_escape }}</description>
<pubDate>{{ post.date | date_to_rfc822 }}</pubDate>
<link>{{ post.url | prepend: site.baseurl | prepend: site.url }}</link>
<guid isPermaLink="true">{{ post.url | prepend: site.baseurl | prepend: site.url }}</guid>
{% for tag in post.tags %}
<category>{{ tag | xml_escape }}</category>
{% endfor %}
{% for cat in post.categories %}
<category>{{ cat | xml_escape }}</category>
{% endfor %}
</item>
{% endfor %}
</channel>
</rss>
认证完成以后就会有一个小勾了
分享
这是我多年积攒下来的一些订阅源,直接用Follow就可以分享,甚至还可以出售
> https://app.follow.is/share/lists/104444993002142720
看起来似乎Follow想通过这种订阅或者说活跃转化成对应的虚拟货币进而维持或者是激励??
这里是我自己的归类,似乎和上面的一样,只是订阅这个人可以订阅到他设置的所有订阅源?
> https://app.follow.is/share/users/101522814280570880
看了下RSSHub,在这里竟然是有人提供RSSHub实例然后收费,帮你爬取一些内容,普通人部署了RSSHub也可以加入其中,目前官方应该是免费给你用的,可能某一天官方就不免费了,就要你自己去爬取内容了?那我为什么不用TTRSS,直接全集成好了,还不要去给别人掏钱,费这个鸟劲。
Summary
目前Follow已经是公测阶段,可以免费加入了,但是功能不是完全体的,要完全功能还是得邀请码激活,这个比较麻烦,我也是问人要了一个才能导入甚至分享订阅的
目前看Follow有点意思,但是不多,比我的TTRSS其实没好多少,很多页面也是完全爬不到,还得配合RSSHub来一起使用抓取内容,那我感觉就没啥必要了,不能直接整合这个东西,体验上还是差一点的
目前看起来这个生态只是个金字塔,只有顶端流量大的人有的赚,其他人只能被收割,看起来并不是一个能循环起来的东西。前一段时间的火爆,现在很少看到相关内容了,估计后续生态起不来依然会沉寂到死
他只能对现有RSS的订阅再次划分,而不能产生新的内容,甚至想依靠这种划分来收割一波,信息差这么好利用吗,而至于你订阅源是否能得到利益,那就很难说了,订阅源作为核心的产出者,没有得到应有的流量,反而把利润分给了分发者和服务部署的人。生产者和消费者被剥削了,中介从中获利?
Quote
> https://app.follow.is/share/lists/104444993002142720
</description>
<category>RSS</category>
</item>
<item>
<title>nRF-Kconfig插件解析</title>
<description><h2 id="foreword">Foreword</h2>
<p>之前刚好看过一点VScode插件原理,这里就能用上了,看一下nRF-Kconfig是怎么实现的,改一个通用版本来,方便大家使用</p>
<h2 id="nrf-kconfig">nRF-Kconfig</h2>
<p>Kconfig插件的安装目录在这里</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\用户名.vscode\extensions\nordic-semiconductor.nrf-kconfig-2024.12.13 </code></pre></div></div>
<p>实际这个插件也没有加密或者混淆什么的,可以直接看</p>
<h3 id="尝试build">尝试build</h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"vscode:prepublish"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build && npm run changelog"</span><span class="p">,</span><span class="w"> </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rimraf dist && tsx ./scripts/build.js –production"</span><span class="p">,</span><span class="w"> </span><span class="nl">"watch"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ./scripts/build.js –watch"</span><span class="p">,</span><span class="w"> </span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ./scripts/build.js –watch"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type-check"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsc –noEmit"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts nrf-kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lintfix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts –fix nrf-kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"changelog"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/changelog.ts kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"copyright"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run –prefix ../ copyright"</span><span class="p">,</span><span class="w"> </span><span class="nl">"buildHeaderFiles"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/buildHeaderFiles.ts"</span><span class="p">,</span><span class="w"> </span><span class="nl">"update-build-number"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node ../scripts/updateBuildNumber.js kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"copy-css"</span><span class="p">:</span><span class="w"> </span><span class="s2">"copyfiles </span><span class="se">\"</span><span class="s2">src//.css</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">src/**/.scss</span><span class="se">\"</span><span class="s2"> out/nrf-kconfig && copyfiles </span><span class="se">\"</span><span class="s2">../common/src//.css</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">../common/src/**/.scss</span><span class="se">\"</span><span class="s2"> out/common"</span><span class="p">,</span><span class="w"> </span><span class="nl">"build-tests"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsc -p . –outDir out"</span><span class="p">,</span><span class="w"> </span><span class="nl">"watch-tests"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsc -p . -w –outDir out"</span><span class="p">,</span><span class="w"> </span><span class="nl">"pretest"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build-tests && npm run build && npm run copy-css"</span><span class="p">,</span><span class="w"> </span><span class="nl">"test"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node ./out/nrf-kconfig/test/runTests.js"</span><span class="w"> </span><span class="p">}</span><span class="err">,</span><span class="w"> </span></code></pre></div></div>
<p>直接使用Package.js中的script部分,可以直接build或者prepublish</p>
<p>可能会遇到下面的错误</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm : 无法加载文件 D:\nodejs\npm.ps1,因为在此系统上禁止运行脚本 </code></pre></div></div>
<p>查看脚本允许策略,可以看到返回值是严格,这样不允许npm调用ps1的脚本</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">Get-ExecutionPolicy</span><span class="w"> </span></code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102163319195.png" alt="image-20250102163319155" /></p>
<p>修改策略,改为允许远程运行</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">Set-ExecutionPolicy</span><span class="w"> </span><span class="nx">RemoteSigned</span><span class="w"> </span></code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102163501426.png" alt="image-20250102163501393" /></p>
<p>修改完成,再次进行build,正常了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102163648430.png" alt="image-20250102163648400" /></p>
<p>依然提示缺少rimraf命令,但是至少说明脚本跑起来了,这个问题后面再处理</p>
<p>安装一下rimraf就行了,实际上后续还会有tsx也缺少,一起都安装了</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">–save</span> rimraf npm <span class="nb">install</span> <span class="nt">–save</span> tsx </code></pre></div></div>
<p>继续往下跑就会发现,这样copy的代码缺少一个script文件夹,这里面包含了各种打包的脚本文件,这里只能先到此为止了</p>
<h3 id="源码分析">源码分析</h3>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"contributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">这里注册了各种使用的按钮</span><span class="w">
</span><span class="nl">"commands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.add"</span><span class="p">,</span><span class="w">
</span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kconfig: Add build folder"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.showGui"</span><span class="p">,</span><span class="w">
</span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kconfig: Show Config GUI"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.goToSymDefinition"</span><span class="p">,</span><span class="w">
</span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Go to Symbol Definition"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.expandSubtree"</span><span class="p">,</span><span class="w">
</span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Expand Subtree"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.collapseSubtree"</span><span class="p">,</span><span class="w">
</span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Collapse Subtree"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.ctx.setActive"</span><span class="p">,</span><span class="w">
</span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Set Active Context"</span><span class="p">,</span><span class="w">
</span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"$(edit)"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"menus"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"webview/context"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.goToSymDefinition"</span><span class="p">,</span><span class="w">
</span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"canGoToSymDefinition == true"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.expandSubtree"</span><span class="p">,</span><span class="w">
</span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"canExpandSubtree == true"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.collapseSubtree"</span><span class="p">,</span><span class="w">
</span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"canCollapseSubtree == true"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"commandPalette"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.goToSymDefinition"</span><span class="p">,</span><span class="w">
</span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"never"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.expandSubtree"</span><span class="p">,</span><span class="w">
</span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"never"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.collapseSubtree"</span><span class="p">,</span><span class="w">
</span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"never"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"view/item/context"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"editor/context"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"submenus"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">这里是一些这个插件的一些设置参数</span><span class="w">
</span><span class="nl">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Settings for nRF Kconfig"</span><span class="p">,</span><span class="w">
</span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"kconfig.zephyr.base"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Override location of Zephyr. This is only necessary if you want to use nRF Kconfig by itself and the main extension is not installed."</span><span class="p">,</span><span class="w">
</span><span class="nl">"scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"machine-overridable"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"kconfig.python"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w">
</span><span class="nl">"markdownDescription"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Location of Python executable. Python 3 is required to use nRF Kconfig. If no location is specified, then the PATH
of the current workspace is used."</span><span class="p">,</span><span class="w">
</span><span class="nl">"scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"machine-overridable"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"kconfig.liveValue"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"boolean"</span><span class="p">,</span><span class="w">
</span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Show changed values in conf files as ghost assignments on the same line as their original assignment."</span><span class="p">,</span><span class="w">
</span><span class="nl">"scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application"</span><span class="p">,</span><span class="w">
</span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">这里主要是对文件解析和提示的配置,什么类型文件才会被调用解析器进行解析、高亮、补全等</span><span class="w">
</span><span class="nl">"languages"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">language定义一个数组,每个数组表示一种支持的语言类型</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">id是每个语言的标识符</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig"</span><span class="p">,</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">别名</span><span class="w">
</span><span class="nl">"aliases"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Kconfig"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">特指文件名</span><span class="w">
</span><span class="nl">"filenames"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Kconfig"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">特指符合某些规则的文件名</span><span class="w">
</span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Kconfig."</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">这个里面是语言的具体规则,应该是内容识别的规则(折叠段识别)</span><span class="w">
</span><span class="nl">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./language-configuration.json"</span><span class="p">,</span><span class="w">
</span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"dark"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./dist/images/dark/kconfig.svg"</span><span class="p">,</span><span class="w">
</span><span class="nl">"light"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./dist/images/light/kconfig.svg"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">下面是特指了一些文件内容,相当于是exclude,排除了某些文件</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">单独指定了他们是什么类型的语言文件,防止误识别</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"python"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Kconfig.py"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cmake"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Kconfig.cmake"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"css"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"Kconfig.css"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"properties"</span><span class="p">,</span><span class="w">
</span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"_defconfig"</span><span class="p">,</span><span class="w">
</span><span class="s2">"*.conf"</span><span class="p">,</span><span class="w">
</span><span class="s2">".config"</span><span class="p">,</span><span class="w">
</span><span class="s2">".config.sysbuild"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"viewsWelcome"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"__metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3c214a32-71ea-4549-a492-0f972af076a5"</span><span class="p">,</span><span class="w">
</span><span class="nl">"publisherId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c9e8387e-6a8a-4101-bcc0-52d8bf37ca83"</span><span class="p">,</span><span class="w">
</span><span class="nl">"publisherDisplayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nordic Semiconductor"</span><span class="p">,</span><span class="w">
</span><span class="nl">"targetPlatform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"undefined"</span><span class="p">,</span><span class="w">
</span><span class="nl">"isApplicationScoped"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"isPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"hasPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"installedTimestamp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1734425172758</span><span class="p">,</span><span class="w">
</span><span class="nl">"pinned"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"preRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gallery"</span><span class="p">,</span><span class="w">
</span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="mi">2490675</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">#</span><span class="w"> </span><span class="err">前面安装的几个依赖的模块,这里其实有提示</span><span class="w">
</span><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"rimraf"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^6.0.1"</span><span class="p">,</span><span class="w">
</span><span class="nl">"tsx"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.19.2"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>重点还是看<code class="language-plaintext highlighter-rouge">kconfig.showGui</code>做了些啥</p>
<p>正常应该有一个js调用的脚本,但是没找到,只看到了py文件,找到了py的入口,直接让Copilot帮我分析了一下,大致是启动了一个LSP的语言分析(language symbol phrase)服务,然后这个服务和什么东西用的是rpc通信。</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">"main"</span><span class="p">:</span> <span class="n">args</span> <span class="o">=</span> <span class="n">parse_args</span><span class="p">()</span>
<span class="n">srv</span> <span class="o">=</span> <span class="n">KconfigServer</span><span class="p">()</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stdout</span> <span class="o">=</span> <span class="n">srv</span><span class="p">.</span><span class="n">log</span>
<span class="n">sys</span><span class="p">.</span><span class="n">stderr</span> <span class="o">=</span> <span class="n">srv</span><span class="p">.</span><span class="n">err</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Server starting"</span><span class="p">)</span>
<span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">debug</span><span class="p">:</span>
<span class="n">wait_for_debugger</span><span class="p">()</span>
<span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">log</span><span class="p">:</span>
<span class="n">srv</span><span class="p">.</span><span class="n">logging</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">srv</span><span class="p">.</span><span class="n">loop</span><span class="p">()</span> </code></pre></div></div>
<p>目前看起来这个LSP服务主要是用来做代码提示、符号信息、Kconfig内容查找?,不知道和具体文件解析有没有关系。</p>
<p>syntaxes下的kconfig.tmGrammar.json是kconfig高亮的配置文件</p>
<p>目前看应该还有一个前端或者是什么东西,通过rpc通信,将符号信息送给Kconfig进行分析,然后返回对应的信息</p>
<p>比如平常常用的查找定义,客户端发送请求,指定文件和符号所在行和字符(这里为啥不用指定字符的长度?不是很懂)</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"textDocument/definition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"textDocument"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:///path/to/file"</span><span class="p">},</span><span class="w"> </span><span class="nl">"position"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"line"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="nl">"character"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div>
<p>KconfigServer接收请求并处理</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="n">handler</span><span class="p">(</span><span class="s">‘textDocument/definition’</span><span class="p">)</span> <span class="k">def</span> <span class="nf">handle_definition</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">params</span><span class="p">):</span> <span class="n">sym</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_sym</span><span class="p">(</span><span class="n">params</span><span class="p">)</span> <span class="k">if</span> <span class="n">sym</span><span class="p">:</span> <span class="k">return</span> <span class="n">loc</span><span class="p">(</span><span class="n">sym</span><span class="p">)</span> </code></pre></div></div>
<p>KconfigServer接着发送响应</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:///path/to/definition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"range"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"line"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="nl">"character"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">},</span><span class="w"> </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"line"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="nl">"character"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div>
<p>客户端接收并处理响应,跳转到符号定义位置,那么这次通信就算完成了</p>
<p>但是这个问题,似乎就是nRF Kconfig插件其实没加载,表面上看安装了,但是真正的加载和注册命令是需要extension的其他插件来完成的,虽然代码里写着独立运行,实际上并不可行</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="err">configuration:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">title:</span><span class="w"> </span><span class="err">‘nRF</span><span class="w"> </span><span class="err">Kconfig’</span><span class="p">,</span><span class="w">
</span><span class="err">type:</span><span class="w"> </span><span class="err">‘object’</span><span class="p">,</span><span class="w">
</span><span class="err">description:</span><span class="w"> </span><span class="err">‘Settings</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">nRF</span><span class="w"> </span><span class="err">Kconfig’</span><span class="p">,</span><span class="w">
</span><span class="err">properties:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">‘kconfig.zephyr.base’:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">type:</span><span class="w"> </span><span class="err">‘string’</span><span class="p">,</span><span class="w">
</span><span class="err">description:</span><span class="w">
</span><span class="err">‘Override</span><span class="w"> </span><span class="err">location</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">Zephyr.</span><span class="w"> </span><span class="err">This</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">only</span><span class="w"> </span><span class="err">necessary</span><span class="w"> </span><span class="err">if</span><span class="w"> </span><span class="err">you</span><span class="w"> </span><span class="err">want</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">use</span><span class="w"> </span><span class="err">nRF</span><span class="w"> </span><span class="err">Kconfig</span><span class="w"> </span><span class="err">by</span><span class="w"> </span><span class="err">itself</span><span class="w"> </span><span class="err">and</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">main</span><span class="w"> </span><span class="err">extension</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">not</span><span class="w"> </span><span class="err">installed.’</span><span class="p">,</span><span class="w">
</span><span class="err">scope:</span><span class="w"> </span><span class="err">‘machine-overridable’</span><span class="p">,</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">‘kconfig.python’:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">type:</span><span class="w"> </span><span class="err">‘string’</span><span class="p">,</span><span class="w">
</span><span class="err">markdownDescription:</span><span class="w">
</span><span class="err">‘Location</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">Python</span><span class="w"> </span><span class="err">executable.</span><span class="w"> </span><span class="err">Python</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">required</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">use</span><span class="w"> </span><span class="err">nRF</span><span class="w"> </span><span class="err">Kconfig.</span><span class="w"> </span><span class="err">If</span><span class="w"> </span><span class="err">no</span><span class="w"> </span><span class="err">location</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">specified</span><span class="p">,</span><span class="w"> </span><span class="err">then</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">PATH
</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">current</span><span class="w"> </span><span class="err">workspace</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">used.’</span><span class="p">,</span><span class="w">
</span><span class="err">scope:</span><span class="w"> </span><span class="err">‘machine-overridable’</span><span class="p">,</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="err">‘kconfig.liveValue’:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="err">type:</span><span class="w"> </span><span class="err">‘boolean’</span><span class="p">,</span><span class="w">
</span><span class="err">description:</span><span class="w">
</span><span class="err">‘Show</span><span class="w"> </span><span class="err">changed</span><span class="w"> </span><span class="err">values</span><span class="w"> </span><span class="err">in</span><span class="w"> </span><span class="err">conf</span><span class="w"> </span><span class="err">files</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">ghost</span><span class="w"> </span><span class="err">assignments</span><span class="w"> </span><span class="err">on</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">same</span><span class="w"> </span><span class="err">line</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">their</span><span class="w"> </span><span class="err">original</span><span class="w"> </span><span class="err">assignment.’</span><span class="p">,</span><span class="w">
</span><span class="err">scope:</span><span class="w"> </span><span class="err">‘application’</span><span class="p">,</span><span class="w">
</span><span class="err">default:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">}</span><span class="err">,</span><span class="w">
</span></code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102193317417.png" alt="image-20250102193317336" /></p>
<p><code class="language-plaintext highlighter-rouge">contributes.ts</code>中主要是用TS写的这个<code class="language-plaintext highlighter-rouge">contributes</code>的字段内容,打包过程中会被用来替换package.json中的内容。而<code class="language-plaintext highlighter-rouge">submenus</code>则是由build脚本填充的,由于缺少这一块,所以这里就空了</p>
<h3 id="流程梳理">流程梳理</h3>
<p><code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>中的<code class="language-plaintext highlighter-rouge">KconfigServer</code>类实现了一个Kconfig语言服务器协议(LSP)服务器。这个服务器通常由一个支持LSP的客户端(如Visual Studio Code或其他支持LSP的编辑器)调用和使用。以下是调用和使用<code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>的典型流程:</p>
<h4 id="1-客户端如vs-code启动lsp服务器">1. 客户端(如VS Code)启动LSP服务器</h4>
<p>当用户在支持LSP的编辑器中打开一个Kconfig文件时,编辑器会根据配置启动相应的LSP服务器。</p>
<p>在VS Code中,这通常通过<code class="language-plaintext highlighter-rouge">package.json</code>中的<code class="language-plaintext highlighter-rouge">contributes</code>和<code class="language-plaintext highlighter-rouge">activationEvents</code>字段配置。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{ "contributes": { "languages": [ { "id": "kconfig", "extensions": [".kconfig"], "configuration": "./language-configuration.json" } ], "commands": [ { "command": "extension.kconfigServer", "title": "Start Kconfig Server" } ] }, "activationEvents": [ "onLanguage:kconfig" ] } </code></pre></div></div>
<ul> <li>这里的分析推敲,其实反向验证了package.json内的内容</li> </ul>
<h4 id="2-启动lsp服务器">2. 启动LSP服务器</h4>
<p>当满足激活条件(如打开Kconfig文件)时,编辑器会启动LSP服务器进程。</p>
<p>这个进程会运行<code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>中的代码,创建并启动<code class="language-plaintext highlighter-rouge">KconfigServer</code>实例。</p>
<h4 id="3-客户端与lsp服务器通信">3. 客户端与LSP服务器通信</h4>
<p>启动后,客户端和LSP服务器通过JSON-RPC协议进行通信。</p>
<p>客户端发送各种LSP请求(如<code class="language-plaintext highlighter-rouge">textDocument/didOpen</code>、<code class="language-plaintext highlighter-rouge">textDocument/didChange</code>、<code class="language-plaintext highlighter-rouge">textDocument/completion</code>等)到服务器,<code class="language-plaintext highlighter-rouge">KconfigServer</code>类处理这些请求,并返回相应的响应。</p>
<h4 id="4-处理请求和响应">4. 处理请求和响应</h4>
<p><code class="language-plaintext highlighter-rouge">KconfigServer</code>类中的方法(如<code class="language-plaintext highlighter-rouge">handle_initialize</code>、<code class="language-plaintext highlighter-rouge">handle_completion</code>、<code class="language-plaintext highlighter-rouge">handle_definition</code>等)处理来自客户端的请求。</p>
<h4 id="5-服务器发送通知">5. 服务器发送通知</h4>
<p>除了处理请求,<code class="language-plaintext highlighter-rouge">KconfigServer</code>还可以主动发送通知给客户端,例如发布诊断信息(<code class="language-plaintext highlighter-rouge">publish_diags</code>方法)或通知状态变化(<code class="language-plaintext highlighter-rouge">notify_status</code>方法)。</p>
<h3 id="nrf-connect-extension-pack">nRF-connect-extension-pack</h3>
<p>由于上面所有的逻辑都是copilot直接给出来的,实际在这份工程中并没有找到对应的代码,所以不是清楚是怎么一步步调用<code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>的。怀疑这部分的JS代码是直接内嵌在了nRF的整体扩展中,所以单独只安装一个kconfig插件是无法直接显示kconfig的结果的,必须要配合大插件包才能正常启动</p>
<p>这里再把<code class="language-plaintext highlighter-rouge">nordic-semiconductor.nrf-connect-extension-pack-2024.9.5</code>的包拿出来分析一下,看看具体内容是怎么来的</p>
<p>看了一下pack的package.json,发现他好像只是一个整合包,实际并没有什么具体的代码</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nrf-connect-extension-pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"displayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Connect for VS Code Extension Pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Recommended extensions for development with the nRF Connect SDK"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisher"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nordic-semiconductor"</span><span class="p">,</span><span class="w"> </span><span class="nl">"homepage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/index.html"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"images/icon.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024.9.5"</span><span class="p">,</span><span class="w"> </span><span class="nl">"bugs"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://devzone.nordicsemi.com"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"engines"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"vscode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.80.0"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"categories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Extension Packs"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"extensionPack"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-connect"</span><span class="p">,</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-terminal"</span><span class="p">,</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-devicetree"</span><span class="p">,</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-kconfig"</span><span class="p">,</span><span class="w"> </span><span class="s2">"ms-vscode.cpptools"</span><span class="p">,</span><span class="w"> </span><span class="s2">"trond-snekvik.gnu-mapfiles"</span><span class="p">,</span><span class="w"> </span><span class="s2">"twxs.cmake"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./dist/extension.js"</span><span class="p">,</span><span class="w"> </span><span class="nl">"activationEvents"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"*"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"vscode:prepublish"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build – –notypecheck"</span><span class="p">,</span><span class="w"> </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rimraf dist && tsx ./scripts/build.js –production"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts connect-extension-pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lintfix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts –fix connect-extension-pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"copyright"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run –prefix ../ copyright"</span><span class="p">,</span><span class="w"> </span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ./scripts/build.js –watch"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"__metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"50d64e34-3ee8-4e0c-b79d-97a3eab4581d"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisherId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c9e8387e-6a8a-4101-bcc0-52d8bf37ca83"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisherDisplayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nordic Semiconductor"</span><span class="p">,</span><span class="w"> </span><span class="nl">"targetPlatform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"undefined"</span><span class="p">,</span><span class="w"> </span><span class="nl">"isApplicationScoped"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"isPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"hasPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"installedTimestamp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1734424781886</span><span class="p">,</span><span class="w"> </span><span class="nl">"pinned"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"preRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gallery"</span><span class="p">,</span><span class="w"> </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="mi">77034</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div>
<p>再仔细看一下extension.js的代码</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objCreate</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objProperty</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objPropertyDescriptor</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyDescriptor</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objPropertyNames</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objGet</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getPrototypeOf</span><span class="p">,</span> <span class="nx">objHasOP</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">t</span> <span class="k">in</span> <span class="nx">e</span><span class="p">)</span> <span class="nx">objProperty</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">t</span><span class="p">,</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="nx">e</span><span class="p">[</span><span class="nx">t</span><span class="p">],</span> <span class="na">enumerable</span><span class="p">:</span> <span class="o">!</span><span class="mi">0</span> <span class="p">})</span> <span class="p">},</span> <span class="nx">d</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">e</span><span class="p">,</span> <span class="nx">t</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">e</span> <span class="o">&&</span> <span class="k">typeof</span> <span class="nx">e</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span> <span class="o">||</span> <span class="k">typeof</span> <span class="nx">e</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">)</span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="k">of</span> <span class="nx">objPropertyNames</span><span class="p">(</span><span class="nx">e</span><span class="p">))</span> <span class="o">!</span><span class="nx">objHasOP</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">&&</span> <span class="nx">i</span> <span class="o">!==</span> <span class="nx">t</span> <span class="o">&&</span> <span class="nx">objProperty</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">e</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="na">enumerable</span><span class="p">:</span> <span class="o">!</span><span class="p">(</span><span class="nx">s</span> <span class="o">=</span> <span class="nx">objPropertyDescriptor</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span> <span class="nx">i</span><span class="p">))</span> <span class="o">||</span> <span class="nx">s</span><span class="p">.</span><span class="nx">enumerable</span> <span class="p">});</span> <span class="k">return</span> <span class="nx">n</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">f</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">e</span><span class="p">,</span> <span class="nx">t</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span><span class="nx">t</span> <span class="o">=</span> <span class="nx">n</span> <span class="o">!=</span> <span class="kc">null</span> <span class="p">?</span> <span class="nx">objCreate</span><span class="p">(</span><span class="nx">objGet</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="p">:</span> <span class="p">{},</span> <span class="nx">d</span><span class="p">(</span><span class="nx">e</span> <span class="o">||</span> <span class="o">!</span><span class="nx">n</span> <span class="o">||</span> <span class="o">!</span><span class="nx">n</span><span class="p">.</span><span class="nx">__esModule</span> <span class="p">?</span> <span class="nx">objProperty</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="dl">"</span><span class="s2">default</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="nx">n</span><span class="p">,</span> <span class="na">enumerable</span><span class="p">:</span> <span class="o">!</span><span class="mi">0</span> <span class="p">})</span> <span class="p">:</span> <span class="nx">t</span><span class="p">,</span> <span class="nx">n</span><span class="p">)),</span> <span class="nx">C</span> <span class="o">=</span> <span class="nx">n</span> <span class="o">=></span> <span class="nx">d</span><span class="p">(</span><span class="nx">objProperty</span><span class="p">({},</span> <span class="dl">"</span><span class="s2">__esModule</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="o">!</span><span class="mi">0</span> <span class="p">}),</span> <span class="nx">n</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="p">{};</span> </code></pre></div></div>
<p>使用 b函数将属性从 <code class="language-plaintext highlighter-rouge">source</code> 对象定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,而不是直接使用 <code class="language-plaintext highlighter-rouge">source</code> 对象,有几个好处:</p>
<p><strong>封装和模块化</strong>:</p>
<ul> <li>通过将属性定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,可以更好地封装和模块化代码。这样可以避免直接暴露 <code class="language-plaintext highlighter-rouge">source</code> 对象的内部实现细节。</li> </ul>
<p><strong>动态扩展对象</strong>:</p>
<ul> <li>可以动态地将属性添加到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,而不需要修改 <code class="language-plaintext highlighter-rouge">source</code> 对象。这在需要扩展对象功能时非常有用。</li> </ul>
<p><strong>控制属性访问</strong>:</p>
<ul> <li>使用 <code class="language-plaintext highlighter-rouge">Object.defineProperty</code> 可以精确控制属性的行为,例如设置属性为只读、可枚举等。在这个例子中,属性被定义为 getter 函数,并且是可枚举的。</li> </ul>
<p><strong>避免命名冲突</strong>:</p>
<ul> <li>如果直接使用 <code class="language-plaintext highlighter-rouge">source</code> 对象,可能会与现有对象的属性发生命名冲突。通过将属性定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,可以避免这种冲突。</li> </ul>
<p><strong>灵活性</strong>:</p>
<ul> <li>可以根据需要选择性地将某些属性定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,而不是将整个 <code class="language-plaintext highlighter-rouge">source</code> 对象暴露出来。</li> </ul>
<p>就是通过Object.defineProperty实现一个代理类,后续代码基本都是围绕这个目标来做的。</p>
<p>最终把一些按钮或者触发器注册进了VSCode,这些id就对应上了具体的界面按钮和后续的事件触发机制</p>
<p>可以看到就是在这里把kconfig相关的指令注册进去了,前面报的kconfig不存在,也是因为connect没有启动,这里没有加载</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250103120131658.png" alt="image-20250103120131588" /></p>
<p>所以还是得去看connect部分的代码</p>
<h3 id="nordic-semiconductornrf-connect">nordic-semiconductor.nrf-connect</h3>
<p>查看extension.js,格式化以后发现这个js的行数有12w,这就有点离谱了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250103163028743.png" alt="image-20250103163028633" /></p>
<p>copilot直接放弃工作了,让他读一下直接不会了</p>
<p>看了一下打包文件夹是dist,应该是套在某个框架上写完以后重新打包成了一个.js,所以这个内容就很难反向查了</p>
<h2 id="summary">Summary</h2>
<p>还是重写一个更快,这里能借鉴的只有Kconfig LSP这个部分了</p>
</description>
<category>build</category>
</item>
<item>
<title>Zephyr build 架构</title>
<description><h2 id="foreword">Foreword</h2>
<p>Zephyr 工程的整个构建体系是怎样的,这里做一个具体的分析和学习</p>
<p>最近小米的Vele 也开源了,不过由于缺少文档,而且架构非常庞大,光是仓库就传了两三百个,想看明白不太容易,但是基础的CMake、Kconfig等等全都有。</p>
<h2 id="cmake">CMake</h2>
<ol> <li><strong>创建构建目录</strong>:保持源代码目录整洁。</li> <li><strong>使用 CMake 生成构建文件</strong>:配置项目并生成适合平台的构建文件。</li> <li><strong>编译和构建</strong>:使用生成的构建文件执行编译和构建。</li> <li><strong>清理构建文件</strong>:删除中间文件和目标文件。</li> <li><strong>重新配置和构建</strong>:处理项目设置的更改。</li> </ol>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191052534.png" alt="img" /></p>
<p>CMake 推荐使用 <strong>“Out-of-source”</strong> 构建方式,即将构建文件放在源代码目录之外的独立目录中</p>
<h3 id="cmake例程">CMake例程</h3>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191134890.png" alt="image-20241219113430856" /></p>
<p>一个比较简单的CMake例程</p>
<blockquote> <p>https://github.com/elmagnificogi/MyTools/tree/master/CMake/MyProject1</p> </blockquote>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include <iostream> </span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"Hello, CMake!"</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div>
<p>cpp文件</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifndef MAIN_H #define MAIN_H </span> <span class="c1">// Declarations of module functions</span>
<span class="cp">#endif // MAIN_H </span></code></pre></div></div>
<p>头文件</p>
<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 指定最低 CMake 版本</span>
<span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.10<span class="p">)</span>
<span class="c1"># 定义项目名称和版本</span>
<span class="nb">project</span><span class="p">(</span>MyProject1 VERSION 1.0<span class="p">)</span>
<span class="c1"># 设置 C++ 标准</span> <span class="nb">set</span><span class="p">(</span>CMAKE_CXX_STANDARD 11<span class="p">)</span> <span class="nb">set</span><span class="p">(</span>CMAKE_CXX_STANDARD_REQUIRED ON<span class="p">)</span>
<span class="nb">include_directories</span><span class="p">(</span><span class="si">${</span><span class="nv">PROJECT_SOURCE_DIR</span><span class="si">}</span>/include<span class="p">)</span>
<span class="c1"># 创建可执行文件目标</span> <span class="nb">add_executable</span><span class="p">(</span>MyExecutable <span class="si">${</span><span class="nv">PROJECT_SOURCE_DIR</span><span class="si">}</span>/src/main.cpp<span class="p">)</span> </code></pre></div></div>
<p>比较简单的CMakeLists.txt定义</p>
<p>新建build目录,将源码和编译路径分开</p>
<p>生成一个MinGW的Makefiles文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D:\CMake\bin\cmake.exe .. -G "MinGW Makefiles" </code></pre></div></div>
<ul> <li>不指定的话,默认是VS的工程文件</li> </ul>
<p>编译</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mingw32-make.exe </code></pre></div></div>
<p>测试</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I:\cmake\CMakeTutorialCN\MyProject\build\src>MyExecutable.exe Hello, CMake! </code></pre></div></div>
<p>稍微复杂一些,但是包含了库,移除了测试库,windows下拉不到这个测试库</p>
<blockquote> <p>https://github.com/elmagnificogi/MyTools/tree/master/CMake/MyProject0</p> </blockquote>
<p>CMake确实稍微步骤多了一些,组织编排就要跑一会,还是这么简单的工程</p>
<h2 id="基于nrf-untiled例程分析">基于nRF untiled例程分析</h2>
<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.20.0<span class="p">)</span> <span class="c1"># 寻找依赖包Zephyr</span> <span class="c1"># $ENV{Zephyr_BASE} 设置了 Zephyr 的根目录路径</span> <span class="nb">find_package</span><span class="p">(</span>Zephyr REQUIRED HINTS $ENV{Zephyr_BASE}<span class="p">)</span>
<span class="nb">project</span><span class="p">(</span>untitled<span class="p">)</span>
<span class="c1"># target_sources 指令用于为 CMake 目标添加源文件。这种方法的优点在于它可以将源文件添加到目标中,并且可以在 CMake 的生成阶段自动更新。</span> <span class="c1"># target: 要添加源文件的目标,通常是通过 add_executable 或 add_library 定义的目标。</span> <span class="c1"># INTERFACE: 指定这些源文件对其他目标是可见的,但不会包含在目标本身的构建中。这通常用于接口库。</span> <span class="c1"># PUBLIC: 指定这些源文件对其他链接到该目标的目标是可见的,同时也包含在目标本身的构建中。</span> <span class="c1"># PRIVATE: 指定这些源文件仅对目标本身可见,不会传播到链接到该目标的其他目标。</span> <span class="nf">target_sources</span><span class="p">(</span>app PRIVATE src/main.c<span class="p">)</span> </code></pre></div></div>
<p>编译时执行的 <code class="language-plaintext highlighter-rouge">build.py</code> 脚本中引用了 <code class="language-plaintext highlighter-rouge">Zephyr\scripts\west_commands\Zephyr_ext_common.py</code>,在这个脚本中设置了 <strong>Zephyr_BASE</strong> 的宏。</p>
<ul> <li>宏的值被设置为了之前sdk安装路径,比如I:\ncs\v2.7.0\Zephyr</li> </ul>
<p>untiled内容比较少,还是需要去看Zephyr内的CMake</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191553570.png" alt="image-20241219155330509" /></p>
<p>先看一眼nRF的SDK包构成</p>
<ul> <li>west 是Zephyr的生成工具</li> <li>modules里面是hal、lab库、文件系统、加密等等模块</li> <li>test测试库</li> <li>Zephyr的主要内容</li> </ul>
<p>Zephyr的包主要存储在这里,通过上面的CMake搜索到这里</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191604069.png" alt="image-20241219160421036" /></p>
<p>Zephyr的CMake主要调用逻辑</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Zephyr/share/Zephyr-package/cmake/ZephyrConfigVersion.cmake Zephyr/share/Zephyr-package/cmake/Zephyr_package_search.cmake Zephyr/cmake/modules/version.cmake Zephyr/share/Zephyr-package/cmake/ZephyrConfig.cmake </code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20241219163220232.jpeg" alt="img" /></p>
<p>Zephyr主要是两个阶段,配置阶段、编译阶段</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/build-config-phase.svg" alt="Zephyr’s build configuration phase" /></p>
<p>根据这个图来说,最终Kconfig生成了autoconf.h,同时持久化了一个.config配置方便后续使用</p>
<p>Zephyr同时也生成了一个设备树,这个概念基本和Linux一样,都是通过设备树来注册驱动进去的,类似的设备树也有同样的一个.h文件。</p>
<p>最终这部分生成文件和产生的Makefile一起进行最后的编译,生成对应固件</p>
<h2 id="自定义kconfig">自定义Kconfig</h2>
<p>在untiled下面新建Kconfig文件,输入以下内容</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menu "Test Params setting" config TEST_ENABLE bool "Enable test work" default n help Will print debug information if enable.
config TEST_SHOW_STRING string "The show string info" default "Test 123"
config TEST_SHOW_INT int "The show int info" range 0 255 default 123
config TEST_TOP_ENABLE bool "Test Top Func" default n help Function Test Top
config TEST_SUB_0_ENABLE bool "Test Sub 0 Func" default n help Function Test Sub 0
config TEST_SUB_1_ENABLE bool "Test Sub 1 Func" default n depends on TEST_TOP_ENABLE help Function Test Sub 1
config TEST_SHOW_SUB_INT int default 456 if TEST_SUB_0_ENABLE && TEST_SUB_1_ENABLE default 123
endmenu
注意一定要引用这个,不然加不进去,并且CMake不了
source "Kconfig.zephyr" </code></pre></div></div>
<p>重新build一下,然后打开Kconfig GUI就能看到对应显示了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102155527462.png" alt="image-20250102155527389" /></p>
<p>这里比较麻烦的地方就是这个工程每次重新打开,这个GUI就打不开了,必须要点开nRF Connect才能重新打开,非常难受</p>
<h2 id="summary">Summary</h2>
<p>工程越大越复杂,还是希望能稍微控制一下复杂度</p>
<p>参考这个教程,但是这个教程写的一坨屎,为什么还会有人点星</p>
<blockquote> <p>https://github.com/shendeguize/CMakeTutorialCN</p> </blockquote>
<h2 id="quote">Quote</h2>
<blockquote> <p>https://blog.csdn.net/qq_37805392/article/details/141337664</p>
<p>https://zhuanlan.zhihu.com/p/705640074</p>
<p>https://docs.Zephyrproject.org/latest/build/index.html</p>
<p>https://blog.csdn.net/My_CSDN_IT/article/details/118180074</p>
<p>https://lgl88911.github.io/2020/03/08/Zephyr%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B%E7%AE%80%E8%BF%B0/</p>
<p>https://www.cnblogs.com/jayant97/articles/17794813.html</p>
</blockquote>
</description>
<category>build</category>
</item>
<item>
<title>配置管理工具之kconfig</title>
<description><h2 id="foreword">Foreword</h2>
<p>绕了这么大一圈,似乎只有kconfig是比较成熟的,能与之相媲美的管理工具很少</p>
<h2 id="kconfig">Kconfig</h2>
<h3 id="安装">安装</h3>
<p>在windows下使用Kconfig,至少得有python,否则界面等内容无法正常显示</p>
<p>python需要先安装这几个包</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python -m pip install windows-curses python -m pip install kconfiglib </code></pre></div></div>
<p>测试安装,可以正常显示命令</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menuconfig -h </code></pre></div></div>
<h3 id="测试">测试</h3>
<p>参考工程sample_1</p>
<blockquote> <p>https://github.com/bobwenstudy/test_kconfig_system</p> </blockquote>
<p>编译所有</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make all </code></pre></div></div>
<p>修改配置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menuconfig </code></pre></div></div>
<p>运行<code class="language-plaintext highlighter-rouge">main.exe</code>就能看到结果了</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hello, world CONFIG_TEST_ENABLE CONFIG_TEST_SHOW_STRING: Test 123 CONFIG_TEST_SHOW_INT: 123 CONFIG_TEST_SUB_0_ENABLE CONFIG_TEST_SHOW_SUB_INT: 123 </code></pre></div></div>
<p>可能会报错,这个是因为curses库他需要终端执行,但是不识别你当前使用的终端,换一个就行了,比如cmd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make 提示Redirection is not supported. </code></pre></div></div>
<p>这个例子里还只有一种config,还不能支持多config配置,可以看到主要就只能生成一个autoconfig.h</p>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">all</span><span class="o">:</span> <span class="nf">main.o</span> gcc main.o <span class="nt">-o</span> main <span class="nl">main.o</span><span class="o">:</span> <span class="nf">main.c autoconfig.h</span> gcc main.c <span class="nt">-c</span> <span class="nt">-o</span> main.o <span class="nl">clean</span><span class="o">:</span> del main.o main.exe
<span class="nl">autoconfig.h</span><span class="o">:</span><span class="nf">.config</span> python ../scripts/kconfig.py Kconfig .config autoconfig.h log.txt .config <span class="nl">.config</span><span class="o">:</span> menuconfig <span class="nl">menuconfig</span><span class="o">:</span> menuconfig </code></pre></div></div>
<h3 id="多config">多config</h3>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 默认编译test1 </span><span class="nv">APP</span> <span class="o">?=</span> app/test1 <span class="nv">OUTPUT_PATH</span> <span class="o">:=</span> output
<span class="nv">INCLUDE_PATH</span> <span class="o">:=</span> <span class="nt">-I</span><span class="nv">$(APP)</span> <span class="nt">-Idriver</span> <span class="nt">-I</span><span class="nv">$(OUTPUT_PATH)</span>
<span class="c"># 主要用户定义的配置是在每个app下的,这里也可以单独提取一个对应的版本目录来做
define user .config setting
</span><span class="nv">USER_CONFIG_SET</span> <span class="o">:=</span> <span class="nv">USER_CONFIG_SET</span> <span class="o">+=</span> <span class="nv">$(APP)</span>/prj.conf
<span class="c"># define menuconfig .config path </span><span class="nv">DOTCONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/.config
<span class="c"># 用户文件追踪
define user merged path
</span><span class="nv">USER_RECORD_CONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/user_record.conf
<span class="c"># define autoconfig.h path </span><span class="nv">AUTOCONFIG_H</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/autoconfig.h
<span class="c">#define Kconfig path </span><span class="nv">KCONFIG_ROOT_PATH</span> <span class="o">:=</span> Kconfig
<span class="c">#For windows work. </span><span class="nv">FIXPATH</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">subst</span> /,<span class="se">\,</span><span class="nv">$1</span><span class="nf">)</span>
<span class="nl">all</span><span class="o">:</span> <span class="nf">$(APP)/main.o driver/driver_test.o</span> gcc <span class="nv">$^</span> <span class="nt">-o</span> <span class="nv">$(OUTPUT_PATH)</span>/main.exe <span class="nl">$(APP)/main.o</span><span class="o">:</span> <span class="nf">$(APP)/main.c</span> gcc <span class="nv">$<</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span> <span class="nl">driver/driver_test.o</span><span class="o">:</span> <span class="nf">driver/driver_test.c $(AUTOCONFIG_H)</span> gcc <span class="nv">$<</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="nl">clean</span><span class="o">:</span> del /q /s <span class="nf">$(</span><span class="nb">call</span> FIXPATH, <span class="nv">$(APP)</span>/main.o driver/driver_test.o <span class="nv">$(OUTPUT_PATH)</span><span class="nf">)</span>
<span class="c"># 根据对应的autoconfig的目录来生成 </span><span class="nl">$(AUTOCONFIG_H)</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> python ../scripts/kconfig.py <span class="nv">$(KCONFIG_ROOT_PATH)</span> <span class="nv">$(DOTCONFIG_PATH)</span> <span class="nv">$(AUTOCONFIG_H)</span> <span class="nv">$(OUTPUT_PATH)</span>/log.txt <span class="nv">$(DOTCONFIG_PATH)</span>
<span class="c"># 从用户的项目路径里拿刀用户定义的config </span><span class="nl">$(USER_RECORD_CONFIG_PATH)</span><span class="o">:</span> <span class="nf">$(USER_CONFIG_SET)</span> <span class="p">@</span><span class="nb">echo </span>Using user config. <span class="c"># create user_record.conf to record current setting. </span> <span class="err">@copy</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$^)</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$@)</span> <span class="c"># create .config by user config setting. </span> <span class="err">python</span> <span class="err">../scripts/kconfig.py</span> <span class="err">–handwritten-input-configs</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> <span class="err">$(DOTCONFIG_PATH)</span> <span class="err">$(AUTOCONFIG_H)</span> <span class="err">$(OUTPUT_PATH)/log.txt</span> <span class="err">$(USER_CONFIG_SET)</span>
<span class="k">export </span><span class="nv">KCONFIG_CONFIG</span><span class="o">=</span><span class="nv">$(DOTCONFIG_PATH)</span> <span class="nl">$(DOTCONFIG_PATH)</span><span class="o">:</span><span class="nf">$(USER_RECORD_CONFIG_PATH)</span> <span class="p">@</span><span class="nb">echo</span> .config updated
<span class="nl">menuconfig</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> <span class="c"># set KCONFIG_CONFIG=$(DOTCONFIG_PATH) </span> <span class="err">menuconfig</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> </code></pre></div></div>
<p>对应这样的配置就可以使用下面的方式来独立生成每个APP的配置,这样就可以同时维护多个版本,而要多一个APP,就再加一个配置就行了</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make APP=app\test1 make APP=app\test2 </code></pre></div></div>
<p>上面这个配置还少了一点,生成的都是同一个文件,同一个目录,互相覆盖,其实可以参考OpenWRT,每次不同配置生成都在不同文件夹里,然后对应文件名就更好了</p>
<h3 id="再次改进">再次改进</h3>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">APP</span> <span class="o">?=</span> app/test1 <span class="nv">OUTPUT_PATH</span> <span class="o">:=</span> output/<span class="nv">$(APP)</span> <span class="nv">TARGET</span> <span class="o">:=</span> main
<span class="nv">INCLUDE_PATH</span> <span class="o">:=</span> <span class="nt">-I</span><span class="nv">$(APP)</span> <span class="nt">-Idriver</span> <span class="nt">-I</span><span class="nv">$(OUTPUT_PATH)</span>
<span class="c"># define user .config setting </span><span class="nv">USER_CONFIG_SET</span> <span class="o">:=</span> <span class="nv">USER_CONFIG_SET</span> <span class="o">+=</span> <span class="nv">$(APP)</span>/prj.conf
<span class="c"># define menuconfig .config path </span><span class="nv">DOTCONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/.config
<span class="c"># define user merged path </span><span class="nv">USER_RECORD_CONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/user_record.conf
<span class="c"># define autoconfig.h path </span><span class="nv">AUTOCONFIG_H</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/autoconfig.h
<span class="c">#define Kconfig path </span><span class="nv">KCONFIG_ROOT_PATH</span> <span class="o">:=</span> Kconfig
<span class="c">#For windows work. </span><span class="nv">FIXPATH</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">subst</span> /,<span class="se">\,</span><span class="nv">$1</span><span class="nf">)</span>
<span class="nl">all</span><span class="o">:</span> <span class="nf">$(APP)/main.o driver/driver_test.o</span> gcc <span class="nv">$^</span> <span class="nt">-o</span> <span class="nv">$(OUTPUT_PATH)</span>/<span class="nv">$(TARGET)</span>.exe
<span class="nl">$(APP)/main.o</span><span class="o">:</span> <span class="nf">$(APP)/main.c | $(OUTPUT_PATH)</span> gcc <span class="nv">$<</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="nl">driver/driver_test.o</span><span class="o">:</span> <span class="nf">driver/driver_test.c $(AUTOCONFIG_H)</span> gcc <span class="nv">$<</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="nl">$(OUTPUT_PATH)</span><span class="o">:</span> <span class="p">@</span><span class="nb">mkdir</span> <span class="nf">$(</span><span class="nb">call</span> FIXPATH, <span class="nv">$@</span><span class="nf">)</span>
<span class="nl">clean</span><span class="o">:</span> del /q /s <span class="nf">$(</span><span class="nb">call</span> FIXPATH, <span class="nv">$(APP)</span>/main.o driver/driver_test.o <span class="nv">$(OUTPUT_PATH)</span><span class="nf">)</span>
<span class="nl">$(AUTOCONFIG_H)</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> python ../scripts/kconfig.py <span class="nv">$(KCONFIG_ROOT_PATH)</span> <span class="nv">$(DOTCONFIG_PATH)</span> <span class="nv">$(AUTOCONFIG_H)</span> <span class="nv">$(OUTPUT_PATH)</span>/log.txt <span class="nv">$(DOTCONFIG_PATH)</span>
<span class="nl">$(USER_RECORD_CONFIG_PATH)</span><span class="o">:</span> <span class="nf">$(USER_CONFIG_SET)</span> <span class="p">@</span><span class="nb">echo </span>Using user config. <span class="c"># create user_record.conf to record current setting. </span> <span class="err">@copy</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$^)</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$@)</span> <span class="c"># create .config by user config setting. </span> <span class="err">python</span> <span class="err">../scripts/kconfig.py</span> <span class="err">–handwritten-input-configs</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> <span class="err">$(DOTCONFIG_PATH)</span> <span class="err">$(AUTOCONFIG_H)</span> <span class="err">$(OUTPUT_PATH)/log.txt</span> <span class="err">$(USER_CONFIG_SET)</span>
<span class="k">export </span><span class="nv">KCONFIG_CONFIG</span><span class="o">=</span><span class="nv">$(DOTCONFIG_PATH)</span> <span class="nl">$(DOTCONFIG_PATH)</span><span class="o">:</span><span class="nf">$(USER_RECORD_CONFIG_PATH)</span> <span class="p">@</span><span class="nb">echo</span> .config updated
<span class="nl">menuconfig</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> <span class="c"># set KCONFIG_CONFIG=$(DOTCONFIG_PATH) </span> <span class="err">menuconfig</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> </code></pre></div></div>
<p>稍微修改一下,增加路径自动创建,这样后续就可以不同配置生成到不同目录中,不会互相覆盖了。同时也自动适配生成的文件名和路径分割符</p>
<p>当然这个Makefile还是有一些缺点,比如所有要编译的.c和.o都得一个个加,还是有点傻</p>
<h3 id="可视化">可视化</h3>
<p>Kconfig发展了这么久,难道没有人想着用VScode来做一下可视化嘛,事实上是有的,但是也都有限,不是很通用</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061636312.png" alt="image-20241206163640263" /></p>
<p>第一个插件只能提供kconfig的语法高亮,不能解决显示问题</p>
<p>第二个就是nRF Kconfig,下面的Konfig for Zephyr Project也停止开发,转而使用nRF Kconfig了</p>
<blockquote> <p>https://marketplace.visualstudio.com/items?itemName=nordic-semiconductor.nrf-kconfig</p> </blockquote>
<p>nRF Kconfig想要用起来还稍微有点麻烦,直接启动不了,所以单独开个段落说明如何使用。</p>
<p>至于非VScode的版本,基本上大部分都是依赖kconfiglib的python库完成显示,或者是结合一下TUI、Tkinter等比较轻量化的框架实现,而整体交互逻辑依然离不开比较原始的上下左右菜单式的</p>
<blockquote> <p>https://github.com/ulfalizer/Kconfiglib</p>
<p>https://github.com/cuinixam/kspl</p>
<p>https://github.com/CoryXie/SConf</p> </blockquote>
<h4 id="esp-idf">ESP-IDF</h4>
<p>其他Kconfig,比如ESP-IDF,就可以很好的体现esp的kconfig内容</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061638409.gif" alt="nRF Kconfig GUI" /></p>
<p>但是ESP是专用的,并不适用于其他开发环节或者板子,而且体积很大</p>
<h2 id="nrf-sdk-demo">nRF SDK Demo</h2>
<h3 id="demo体验">Demo体验</h3>
<p>由于要启动kconfig必须要配置完工程才能启动,所以记录一下nRF SDK是怎么启动、编译的</p>
<ul> <li>nRF也从SES逃离了,投入VSCode的怀抱了,我SES反馈的问题,SES官方就改了一个,其他的问题都视而不见,无语</li> </ul>
<p>首先安装nRF Connect Extendtion Pack</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171704070.png" alt="image-20241217170434968" /></p>
<p>其实相当于是插件包,我细看了一下,基本上需要的开发环境他都给你打包整合好了,设备树、Kconfig、Cmake、调试、Flash等等工具都有,相当于就是一个IDE了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171707735.png" alt="image-20241217170753692" /></p>
<p>选择一个工具链,然后等安装完成,这里直接选最新的就行了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171713349.png" alt="image-20241217171334285" /></p>
<p>继续安装SDK,选择一个路径安装,回车后等待完成</p>
<ul> <li>SDK都有点大,会拉很多Tag进来,最好提前准备好翻墙相关内容</li> <li>2.8.0的SDK貌似拉不下来?,所以切换到2.7.0才正常,对应前一步的toolchains也需要调整到2.7.0</li> </ul>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171758016.png" alt="image-20241217175825913" /></p>
<p>新建一个应用,然后添加编译配置,选择板子,构建配置,开始编译</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171844334.png" alt="image-20241217184447275" /></p>
<p>编译完成,但是注意一下新工程里面没有Kconfig,但是ACtion里已经可以显示Kconfig UI了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171846273.png" alt="image-20241217184632182" /></p>
<h3 id="untitled">untitled</h3>
<p>经过测试nRF的Kconfig GUI无法直接打开前面的sample_2工程,显示非nRF工程</p>
<p>这里详细分析一下untitled的默认工程</p>
<p>untitled一共三个文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mian.c CMakeLists.txt prj.conf </code></pre></div></div>
<p>prj.conf中直接是空白的,main中也没啥特殊内容,主要就是CMakeLists可以被识别?</p>
<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.20.0<span class="p">)</span> <span class="nb">find_package</span><span class="p">(</span>Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}<span class="p">)</span>
<span class="nb">project</span><span class="p">(</span>untitled<span class="p">)</span>
<span class="nf">target_sources</span><span class="p">(</span>app PRIVATE src/main.c<span class="p">)</span> </code></pre></div></div>
<p>实际CMakeLists中的内容也非常少</p>
<p>实际把prj.conf直接复制到sample_2中,Kconfig就可以打开了,但是发现打开的还是untitled的config,而不是sample_2的</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412181528609.png" alt="image-20241218152800544" /></p>
<p>关掉其他文件,再打开GUI,就无法启动了。</p>
<p>这里绑定有点深,必须CMakeLists,必须要在build以后才能用GUI,否则无法直接打开GUI界面</p>
<p>而添加Build Config以后,Build文件夹中的内容非常复杂,非常多内容</p>
<h3 id="nordic-semiconductornrf-kconfig">nordic-semiconductor.nrf-kconfig</h3>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412181732021.png" alt="image-20241218173215879" /></p>
<p>看了一眼nRF Kconfig,源码竟然还是python,也就是跑这个必须有对应的python环境,否则还跑不起来,怪不得那个工具链安装好久,原来在下python。python本质上还是调用kconfiglib库来完成kconfig的解析,最后再转成数据送给VS 前端去加载显示</p>
<p>目前看起来源码没有做加密或者混淆,所以要改一个非nRF强关联的版本,应该是比较容易的</p>
<h2 id="vscode-kconfig">vscode-kconfig</h2>
<p>回过头再看Zephyr的kconfig,这个竟然是开源项目,那么只需要搞定他的入口,就可以显示任何kconfig了</p>
<blockquote> <p>https://github.com/trond-snekvik/vscode-kconfig</p> </blockquote>
<p>后续计划一下改出来一个带UI的通用版本</p>
<h2 id="summary">Summary</h2>
<p>目前看只有Zephyr和nRF的Kconfig结合比较深,后续可能还会详细分析一下Zephyr的整体架构是什么样的,整个build是怎么串起来的</p>
<h2 id="quote">Quote</h2>
<blockquote> <p>https://github.com/bobwenstudy/test_kconfig_system</p>
<p>https://stackoverflow.com/questions/68748824/how-to-fix-redirection-is-not-supported</p>
<p>https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/get_started/install.html</p>
<p>https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/installation/install_ncs.html</p> </blockquote>
</description>
<category>build</category>
</item>
<item>
<title>QEMU模拟运行FreeRTOS</title>
<description><h2 id="foreword">Foreword</h2>
<p>测试一下QEMU模拟运行FreeRTOS</p>
<h2 id="qemu">QEMU</h2>
<p>QEMU安装需要先安装MSYS2</p>
<p>直接下载安装</p>
<blockquote> <p>https://www.msys2.org/</p> </blockquote>
<p>安装完成以后,QEMU使用pacman包进行安装</p>
<blockquote> <p>https://www.qemu.org/download/#windows</p> </blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pacman -S mingw-w64-x86_64-qemu </code></pre></div></div>
<p>添加新的环境变量,把刚才安装的路径加进去</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D:\msys64\mingw64\bin </code></pre></div></div>
<p>查看版本,显示正确</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-arm -version QEMU emulator version 9.1.1 Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers </code></pre></div></div>
<h2 id="freertos">FreeRTOS</h2>
<p>选择FreeRTOS 202212.01版本下载,包含所有演示项目的</p>
<blockquote> <p>https://freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/01-About-the-FreeRTOS-kernel/03-Download-freeRTOS/01-DownloadFreeRTOS</p> </blockquote>
<p>这里主要是测试使用<code class="language-plaintext highlighter-rouge">FreeRTOS/Demo/CORTEX_MPS2_QEMU_IAR_GCC/build/gcc</code>这个项目,需要至少arm-eabi-none-gcc的环境和make,否则进行不下去</p>
<p>进入到gcc目录,先编译一下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make </code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061807331.png" alt="image-20241206180747253" /></p>
<p>正常编译完成</p>
<h2 id="测试">测试</h2>
<p>qemu启动模拟</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ./output/RTOSDemo.out -monitor none -nographic -serial stdio </code></pre></div></div>
<p>正常启动,并且task跑起来了,正在输出内容</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061917507.png" alt="image-20241206191724456" /></p>
<p>qemu这里的参数含义</p>
<ul> <li>-machine mps2-an385,这个是ARM的原型版,开发板模型</li> <li>-cpu cortex-m3,指定cpu架构</li> <li>-kernel ./output/RTOSDemo.out 指定使用的镜像文件</li> <li>-monitor none,显示是否要监控,启用的话类似串口,可以使用中断输入输出一些状态信息</li> <li>-nographic,不使用图形界面</li> <li>-serial stdio,串口通过标准终端输入和输出</li> <li>-s -S,链接调试器</li> </ul>
<h2 id="qemu-stm32">QEMU-STM32</h2>
<p>官方QEMU支持的STM32不是很多,而且用起来很麻烦,需要安装两个环境,而且很容易出问题</p>
<p>所以有人打包了QEMU,并且加入了比较多的ST的开源板子,支持的型号比较多</p>
<blockquote> <p>https://github.com/beckus/qemu_stm32</p> </blockquote>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412071552494.png" alt="image-20241207155204423" /></p>
<p>这个QEMU环境需要重新编译,耗时比较多,或者是从Ubuntu里调用,倒是不用打包成exe</p>
<p>bule bill 的工程用QEMU模拟</p>
<blockquote> <p>https://github.com/beckus/stm32_p103_demos</p> </blockquote>
<p>直接编译</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make </code></pre></div></div>
<p>编译完成以后每个demo下面都有对应的bin文件,可以直接拿来模拟使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-arm -M stm32-p103 -kernel main.bin </code></pre></div></div>
<p>同时RT-Thread,他们的IDE里也集成了专属的QEMU版本,也可以快捷完成模拟</p>
<h2 id="summary">Summary</h2>
<p>挺简单的,只是问题QEMU支持的现成芯片、板子比较少,需要自己去找对应的开发板,甚至自己开发,有点麻烦</p>
<h2 id="quote">Quote</h2>
<blockquote> <p>https://blog.csdn.net/weixin_42607526/article/details/142263258</p>
<p>https://freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/03-Supported-devices/04-Demos/03-Emulation-and-simulation/QEMU/freertos-on-qemu-mps2-an385-model</p>
<p>https://www.cnblogs.com/asmer/p/16813129.html</p>
<p>https://www.qemu.org/docs/master/system/arm/stm32.html</p> </blockquote>
</description>
<category>FreeRTOS</category>
<category>QEMU</category>
</item>
<item>
<title>宏管理工具之lite-manager</title>
<description><h2 id="foreword">Foreword</h2>
<p>体验一下群友的宏管理工具</p>
<h2 id="lite-manager">lite-manager</h2>
<blockquote> <p>https://gitee.com/li-shan-asked/lite-manager</p> </blockquote>
<p>群友的宏管理工具,主要在gitee上更新,github更新不及时,release文件可能不能用</p>
<p>主要是用来方便管理宏定义和生成Makefile,但是大部分设置还是要熟悉Makefile本身,你才能完成lm的配置文件编写,本质上并没有变换脚本语言或者什么的流程</p>
<p>lite-manager在这里有点类似于Kconfig</p>
<h3 id="环境">环境</h3>
<p>至少需要一个make和gun c的环境,之前系统里一直有一个MinGW32 13年的版本,gcc大概只有6,编译过不去(后来发现应该不是这个问题)</p>
<p>通过下面的方式在线安装MinGW64</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://github.com/Vuniverse0/mingwInstaller/releases/download/1.2.1/mingwInstaller.exe </code></pre></div></div>
<p>安装完成以后添加环境路径</p>
<p>如果环境里没有多的make,可以把<code class="language-plaintext highlighter-rouge">mingw32-make.exe</code>复制一个改名叫<code class="language-plaintext highlighter-rouge">make.exe</code>,不然最好还是保持原样,否则会影响到系统里其他地方的make使用</p>
<h3 id="链接脚本问题">链接脚本问题</h3>
<p>测试demo,发现无法正常运行,链接脚本无法识别-M的参数</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021915442.png" alt="image-20241202191533410" /></p>
<p>仔细看了一下Makefile,是生产的链接参数就是<code class="language-plaintext highlighter-rouge">-M</code>而不是<code class="language-plaintext highlighter-rouge">-Map</code></p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021916144.png" alt="image-20241202191650116" /></p>
<p>还好lite-manager源码也是有的,修改一下生成脚本</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021916464.png" alt="image-20241202191633425" /></p>
<p>再把生成的lm.exe拖到对应的测试目录下,进行测试,一切正常了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021916406.png" alt="image-20241202191619373" /></p>
<ul> <li>如果不修改lm,make config时也显示不出来当前宏的状态</li> </ul>
<p>先生成Makefile</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./lm.exe -g Makefile -p hello </code></pre></div></div>
<p>然后可以生成config文件,并且查看宏定义情况</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make config </code></pre></div></div>
<p>编译</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make </code></pre></div></div>
<h3 id="规则">规则</h3>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412031107687.png" alt="image-20241203110700613" /></p>
<p>lm.cfg,用来定义宏的定义以及宏之间的关系</p>
<p>proj.cfg,用户定义实际想要的宏</p>
<p>.lm.mk,缓存</p>
<p>config.h,最终处理完约束条件后实际定义出来的宏</p>
<p>lm.cfg中判断条件是自上而下的,遇到冲突点,首先会关闭自身,然后文件之间是从左向右流动的</p>
<p>其中n,表示宏关闭,‘n’是正常使用n这个关键字</p>
<p>同时下面的文件编译的约束也可以同时使用宏来管控</p>
<p>多模块、多组件的工程目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├───build ├───subdirA | ├───mac_a.c | └───lm.cfg ├───subdirB | ├───mac_b.c | └───lm.cfg └───lm.cfg </code></pre></div></div>
<p>可以使用include直接把下面的配置加载进去</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include "subdirA/lm.cfg" include "subdirB/lm.cfg" </code></pre></div></div>
<p>lm的工程实例可以参考这里,有些技巧example里没有用到,可以看看实例是怎么跑的</p>
<blockquote> <p>https://gitee.com/li-shan-asked/ebraid</p> </blockquote>
<p>lite-manager 同时也有类似tui的交互版本,不过作者说比较老、过时,所以不推荐用了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412031159028.png" alt="img" /></p>
<h3 id="makefile解析">Makefile解析</h3>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 自顶向下来看Makefile的设计
首先目标是hello 然后编译输出到build目录
</span><span class="nv">TARGET</span> <span class="o">:=</span> hello <span class="nv">BUILD_DIR</span> <span class="o">:=</span> build
<span class="c"># 定义一个伪指令,主要用来检测是否从lm的config中生成的
简单说基本上只要不是最终生成的目标,那都是伪指令
伪指令主要是完成一些辅助性的工作,比如clean,检测,切换配置,生成配置等等
检测是否有.lm.mk文件
</span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">check_lmmk</span> <span class="c"># wildcard自动寻找.lm.mk的文件 </span><span class="k">ifneq</span> <span class="nv">($(wildcard .lm.mk),)</span> <span class="k">-include</span><span class="sx"> .lm.mk</span> <span class="k">else</span> <span class="nl">check_lmmk</span><span class="o">:</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"Please run ‘make config’"</span> <span class="k">endif</span>
<span class="c"># 主要涉及使用的工具链,比如 arm-none-eabi-, 这里用默认gcc即可 </span><span class="nv">CC_PREFIX</span> <span class="o">?=</span> <span class="nv">CC</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>gcc <span class="c"># 汇编编译的文件来源是C++ 支持的是.S 如果要支持.s 也在这里修改 </span><span class="nv">AS</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>gcc <span class="nt">-x</span> assembler-with-cpp <span class="c"># copy命令 </span><span class="nv">CP</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>objcopy <span class="c"># 文件size section 字段分析 </span><span class="nv">SZ</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>size <span class="c"># 调试 debug的工具 </span><span class="nv">OD</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>objdump <span class="c"># 生成hex文件 </span><span class="nv">HEX</span> <span class="o">=</span> <span class="nv">$(CP)</span> <span class="nt">-O</span> ihex <span class="c"># 生成bin文件 </span><span class="nv">BIN</span> <span class="o">=</span> <span class="nv">$(CP)</span> <span class="nt">-O</span> binary <span class="nt">-S</span> <span class="c"># 以上工具写的比较多,实际是为了以后适配使用,demo里可能没用到 </span> <span class="c"># 定义额外的宏、库、等路径 </span><span class="nv">CFLAGS</span> <span class="o">:=</span> <span class="nv">$(C_PATH)</span> <span class="nv">$(C_DEFINE)</span> <span class="nv">$(C_FLAG)</span> <span class="nv">$(CPP_FLAG)</span> <span class="c"># 同上,定义链接文件的来源和参数
链接时生成map文件
</span><span class="nv">LDFLAGS</span> <span class="o">:=</span> <span class="nv">$(LD_FLAG)</span> <span class="nv">$(LIB_PATH)</span> <span class="nt">-Wl</span>,-Map<span class="o">=</span><span class="nv">$(BUILD_DIR)</span>/<span class="nv">$(TARGET)</span>.map
<span class="c"># 这里定义生成全部的目标 </span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">all</span> <span class="nl">all</span><span class="o">:</span> <span class="nf">$(BUILD_DIR)/$(TARGET).exe elf_info</span>
<span class="c">#获取到所有要编译的目标对象,这里是从build目录中获取所有c和c++目标 </span><span class="nv">OBJECTS</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">addprefix</span> <span class="nv">$(BUILD_DIR)</span>/,<span class="nf">$(</span><span class="nb">notdir</span> <span class="nf">$(</span><span class="nb">patsubst</span> %.c, %.o, <span class="nf">$(</span><span class="nb">patsubst</span> %.cpp, %.o, <span class="nv">$(C_SOURCE)</span><span class="nf">))))</span> <span class="c"># 指定具体的.c文件的搜索路径 </span><span class="err">vpath</span> <span class="err">%.c</span> <span class="err">$(sort</span> <span class="err">$(dir</span> <span class="err">$(C_SOURCE)))</span> <span class="err">vpath</span> <span class="err">%.cpp</span> <span class="err">$(sort</span> <span class="err">$(dir</span> <span class="err">$(C_SOURCE)))</span> <span class="c">#获取到所有要编译的汇编对象 </span><span class="nv">OBJECTS</span> <span class="o">+=</span> <span class="nf">$(</span><span class="nb">addprefix</span> <span class="nv">$(BUILD_DIR)</span>/,<span class="nf">$(</span><span class="nb">notdir</span> <span class="nv">$(ASM_SOURCE:.S=.o)</span><span class="nf">))</span> <span class="err">vpath</span> <span class="err">%.S</span> <span class="err">$(sort</span> <span class="err">$(dir</span> <span class="err">$(ASM_SOURCE)))</span>
<span class="c"># 创建存储.o和.c文件依赖关系的.d文件
-MMD -MP -MF $(@:%.o=%.d) 是利用gcc自动生成依赖关系
将build路径下的所有c文件编译成.o文件
Makefile文件自身也是依赖文件,如果Makefile文件发生变动,也需要重新编译
-Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)):生成临时中间文件
-c 仅仅编译,不进行链接
$@是指这条指令的所有操作对象
$<是第一个文件
</span><span class="nl">$(BUILD_DIR)/%.o</span><span class="o">:</span> <span class="nf">%.c Makefile | $(BUILD_DIR)</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"CC </span><span class="nv">$<</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(CC)</span> <span class="nt">-c</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-MMD</span> <span class="nt">-MP</span> <span class="se">\</span> <span class="nt">-MF</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span><:.c<span class="o">=</span>.d<span class="nf">))</span> <span class="se">\</span> <span class="nt">-Wa</span>,-a,-ad,-alms<span class="o">=</span><span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span><:.c<span class="o">=</span>.lst<span class="nf">))</span> <span class="nv">$<</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="c"># 同上,只不过此处仅编译c++文件 </span><span class="nl">$(BUILD_DIR)/%.o</span><span class="o">:</span> <span class="nf">%.cpp Makefile | $(BUILD_DIR)</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"CC </span><span class="nv">$<</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(CC)</span> <span class="nt">-c</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-MMD</span> <span class="nt">-MP</span> <span class="se">\</span> <span class="nt">-MF</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span><:.cpp<span class="o">=</span>.d<span class="nf">))</span> <span class="se">\</span> <span class="nt">-Wa</span>,-a,-ad,-alms<span class="o">=</span><span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span><:.cpp<span class="o">=</span>.lst<span class="nf">))</span> <span class="nv">$<</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="c"># 同上,此处仅编译.S的汇编文件 </span><span class="nl">$(BUILD_DIR)/%.o</span><span class="o">:</span> <span class="nf">%.S Makefile | $(BUILD_DIR)</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"AS </span><span class="nv">$<</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(AS)</span> <span class="nt">-c</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-MMD</span> <span class="nt">-MP</span> <span class="se">\</span> <span class="nt">-MF</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span><:.S<span class="o">=</span>.d<span class="nf">))</span> <span class="nv">$<</span> <span class="nt">-o</span> <span class="nv">$@</span>
<span class="c"># 生成exe文件 </span><span class="nl">$(BUILD_DIR)/$(TARGET).exe</span><span class="o">:</span> <span class="nf">$(OBJECTS) Makefile</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"LD </span><span class="nv">$@</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(CC)</span> <span class="nv">$(OBJECTS)</span> <span class="nv">$(LDFLAGS)</span> <span class="nt">-o</span> <span class="nv">$@</span> <span class="p">@</span><span class="nv">$(OD)</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nv">$(TARGET)</span>.exe <span class="nt">-xS</span> <span class="o">></span> <span class="nv">$(BUILD_DIR)</span>/<span class="nv">$(TARGET)</span>.s <span class="nv">$@</span> <span class="p">@</span><span class="nb">echo</span> <span class="s1">’‘</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"Build Successful!"</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"ELF </span><span class="nv">$@</span><span class="s2">"</span>
<span class="nl">elf_info</span><span class="o">:</span> <span class="nf">$(BUILD_DIR)/$(TARGET).exe</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"=================================================================="</span> <span class="p">@</span><span class="nv">$(SZ)</span> <span class="nv">$<</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"=================================================================="</span>
<span class="c"># 生成build文件夹 </span><span class="nl">$(BUILD_DIR)</span><span class="o">:</span> <span class="p">@</span><span class="nb">mkdir</span> <span class="nv">$@</span>
<span class="c"># 清除指令 </span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">config clean</span>
<span class="c"># 如果工程配置文件存在,那么就调用lm重新生成配置
这里主要是生成对应的 config.h,不是重新生成Makefile
</span><span class="nl">config</span><span class="o">:</span> <span class="nf">proj.cfg</span> <span class="p">@</span>./lm.exe <span class="nt">–projcfg</span> proj.cfg <span class="nt">–lmcfg</span> lm.cfg <span class="nt">–out</span> config.h <span class="nt">–mem</span> 50 <span class="p">@</span>./lm.exe <span class="nt">–rmdir</span> <span class="nv">$(BUILD_DIR)</span>
<span class="c"># 清空编译 </span><span class="nl">clean</span><span class="o">:</span> <span class="p">@</span>./lm.exe <span class="nt">–rmdir</span> <span class="nv">$(BUILD_DIR)</span>
</code></pre></div></div>
<h3 id="lmcfg解析">lm.cfg解析</h3>
<p>实际在hello的lm.cfg中只有一行,就是加了一个文件进去</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SRC += hello.c </code></pre></div></div>
<p>这里指定了生成的目标是hello</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./lm.exe -g Makefile -p hello </code></pre></div></div>
<p>剩下的Makefile全都靠lm内部自动生成</p>
<p>以stm32f103的工程为例,相当于Makefile是一个通用的Makefile,而lm.cfg 相当于是一个PreMakefile,在这里直接定义Makefile要用的一些参数即可,剩下就是Makefile足够项目通用即可</p>
<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 添加link文件 </span><span class="nv">LDS</span> <span class="o">+=</span> stm32f10x_64KB_flash.ld <span class="c"># 添加汇编文件 </span><span class="nv">ASM</span> <span class="o">+=</span> startup_stm32f10x_md.s <span class="c"># 添加一些关联文件路径 </span><span class="nv">PATH</span> <span class="o">+=</span> lib/cmsis <span class="nv">PATH</span> <span class="o">+=</span> ./
<span class="c"># 添加源文件 </span><span class="nv">SRC</span> <span class="o">+=</span> stm32f10x_it.c <span class="nv">SRC</span> <span class="o">+=</span> main.c
<span class="c"># 增加宏定义 </span><span class="nv">DEFINE</span> <span class="o">+=</span> STM32F10X_HD USE_STDPERIPH_DRIVER <span class="c"># 编译参数 </span><span class="nv">CFLAG</span> <span class="o">+=</span> <span class="nt">-g</span> <span class="nt">-O2</span> <span class="nt">-mthumb</span> <span class="nt">-mcpu</span><span class="o">=</span>cortex-m3 <span class="nt">-march</span><span class="o">=</span>armv7-m <span class="nt">-Wl</span>,-Bstatic <span class="nt">-ffunction-sections</span> <span class="nt">-fdata-sections</span> <span class="nt">-nostdlib</span> <span class="nt">-ffreestanding</span> <span class="c"># 链接参数 </span><span class="nv">LDFLAG</span> <span class="o">+=</span> <span class="nt">-lnosys</span> <span class="nt">-Wl</span>,–cref <span class="nt">-Wl</span>,–no-relax <span class="nt">-Wl</span>,–gc-sections <span class="nt">-ffreestanding</span> <span class="nt">-Wl</span>,-Bstatic <span class="nt">-Wl</span>,–no-warn-rwx-segments <span class="c"># 汇编参数 </span><span class="nv">ASFLAG</span> <span class="o">+=</span> <span class="nt">-Wl</span>, <span class="nt">-Bstatic</span>
<span class="c"># 引入库文件里的lm </span><span class="k">include</span><span class="sx"> "hal/STM32F10x_StdPeriph_Driver/lm.cfg"</span>
</code></pre></div></div>
<h3 id="问题">问题</h3>
<p>使用当前比较通用的<code class="language-plaintext highlighter-rouge">arm-none-eabi</code>的老库</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GNU Arm Embedded Toolchain\10 2021.10 </code></pre></div></div>
<p>STM32F103的工程是无法正常编译的</p>
<p>如果没有看过类似的例子或者直接参考Makefile原工程,不可能上手直接使用</p>
<p>如果使用<code class="language-plaintext highlighter-rouge">Arm GNU Toolchain arm-none-eabi\13.3 rel1</code> 确实可以直接编译过</p>
<p>在不同的使用情景下,lite-manager带来的效果还是有一些差别的,假如你是一个对工程特别熟悉的人,那么对应的lm.cfg或者是proj.cfg中的宏都是可以随手改的。但是并不是每个做工程的人都对整个宏特别熟悉,此时就会出现给了一个不熟悉的人去修改,那么他就得一一对照整个lm.cfg里的约束关系和宏定义的可能,再去设置proj.cfg,如果只是几个宏,那随手就改了(几个宏为什么还要用lm?)</p>
<p>如果是几十个宏,此时要查表,再去决定这个宏可以用什么</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061512950.png" alt="image-20241206151256866" /></p>
<p>这个时候lm的结构就变成负担了,虽然lm可以显示最终结果,但是当你发现结果不对的时候,你依然需要逐项去排查是哪里冲突了</p>
<p>类比传统的Makefile工程来说,lm省去了你手动Makefile里各种if条件的书写,确实简化了一部分Makefile复杂的地方</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061529863.png" alt="image-20241206152950792" /></p>
<p>这里提一点我的想法,有一个比较通用的UI显示,类似于xml或者json的UI化显示,定义好一些规则,直接就能把这种宏配置管理给做了</p>
<ul> <li>事实上ESP32已经实现了这样的效果,VScode中一个插件就能把esp32的配置文件变成可视化的,极大方便了玩家DIY</li> </ul>
<h2 id="summary">Summary</h2>
<p>lite-manager 总体还是挺小的,显示也还凑活,代码是开放的,完全可以自定义一些</p>
<p>总体来说lm简化了Makefile,但是想要使用起来依然需要懂Makefile,需要给项目下的每个文件夹增加配置文件,类比.mk文件</p>
<h2 id="quote">Quote</h2>
<blockquote> <p>https://gitee.com/li-shan-asked/lite-manager/tree/noui/</p>
<p>https://blog.csdn.net/ysu_wangli/article/details/106677907</p> </blockquote>
</description>
<category>build</category>
</item>
<item>
<title>构建工具之xmake</title>
<description><h2 id="foreword">Foreword</h2>
<p>当一套代码兼容了多个软件、硬件,需要面对不同情况下,进行不同的build的时候,就需要额外的工具来辅助完成这一个事情。</p>
<h2 id="通常ide构建">通常IDE构建</h2>
<p>多数情况下,我们使用的各种IDE都有自己的一套UI或者配置文件来完成这个事情。</p>
<p>以VS为例,一般情况对于一个项目的整体构建的配置大概是这样的</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411281628088.png" alt="image-20241128162827009" /></p>
<p>顶级就是Solution 一个解决方案,一个方案下面可能有多个工程共同构成,比如某些工程依赖的库、依赖的测试工程、依赖的一些子应用。解决方案里必然也有一个配置,用来指定各个工程在解决方案级别进行构建时,各个工程适用什么配置来进行组合构建。</p>
<p>单独的工程来说,有一个或者多个配置文件,比如debug和release,这种最常见的,剩下就是对于整个项目的源码文件、库文件、依赖文件、资源文件的组合,可能不同配置用到了不同的组合。</p>
<p>下面实际组织代码和编译的工具链也有可能是不同的,最终把结果输出到指定目录</p>
<p>类似于成品IDE来说,这些配置或者架构都是基础,实际去操作或者修改这些的东西,表面上看都是在UI界面上去填写或者勾选,实际最终都需要通过各种渠道或者命令参数,转化到最底层的工具链中,往往高级的IDE都会封装自有的工具里,不允许外部工具链介入</p>
<h2 id="linux系构建工具">Linux系构建工具</h2>
<p>类似一个<em>Linux Distribution</em>的构建,不仅仅是要编译内核,还有boot、各种app、各种工具链,对应一整套Build工具就非常复杂,而且要统一在一起,才能实现后续的自动化测试或者发布等要求</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411281640490.png" alt="img" /></p>
<p>一般来说buildroot这一套用的比较多一些,很多开发板或者芯片厂商也会使用这一套来维护自己的sdk等</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411281715827.png" alt="image-20241128171517768" /></p>
<p>Buildroot是将一个个工程或者说应用,当作一个package单元,本质上就是把所有package编译好,打包到一起就得到了一个镜像,就可以烧给对应系统使用了</p>
<p>一般工作流程是先拉下来所有需要依赖的源码或者包,然后进行配置,核心配置工具就是menuconfig,他就类似于VS中的整个Solution的配置,规定了各个工程要以什么样的方式进行编译,还有需要使用什么样的编译器来编译,同时对应的编译工具链都会直接拉到工程内,之后进入到每个工程以后还有各自详细的makefile去管理这个模块怎么编译,最终编译完成输出到output中。</p>
<p>kconfig就是用来描述各种包的依赖关系、配置选型的,最终由menuconfig具象化。</p>
<p>一些SDK也是使用了类似的机制,比如ESP-idf,就通过kconfig来选择组件等。</p>
<h2 id="构建底层原理">构建底层原理</h2>
<p>无论是何种方式进行的构建,大都逃不过这么几个构建的组合,文件、宏、脚本。本质上就是选择文件,修改替换文件的部分内容,以及通过宏选择文件内容,就能完成整个构建的目的了。</p>
<p>而负责处理这部分的程序就是Make、Cmake、Ninja、Meson、Xmake等</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411281806253.png" alt="image-20241128180628200" /></p>
<p>说来也比较搞笑,最初大家都用Make Makefile这一套流程,但是写Makefile是个麻烦事,而且随着工程越来越大,平台越来越多,这个东西就更麻烦了,为了解决这个问题,于是引入了CMake,但是CMake也需要用户输入啊,CMake需要写CMakelist。</p>
<p>同时期也出现了make本身很多时候都没有在做代码编译的事情,而是在组织安排代码,并且效率很低,所以出现了Ninja,Ninja借鉴了很多make的问题,做了大幅简化,速度是很快了,不过可能功能上就有些情况不能处理了。而Ninja也要写.Ninja,这也是个麻烦事,还好后续Cmake也支持了Ninja,所以那就都只写CMakelist就行了</p>
<p>在编译构建工具发展的过程中还是存在一些小问题,比如Makefile、.Ninja、CMakelist他们无论怎么简化,他们都有各自的语法,你都要学一下才能写,而且这种小众语法,很多时候不常用,经常要查,对应的他们的官方文档又写的不是很好,很多奇巧淫技都是从别人代码里看来的。</p>
<p>到这里就有人想到了为什么我不能用我自己熟悉语言来写规则呢,那不是更简单?</p>
<p>于是乎就有了xmake,xmake主要用lua来写脚本,lua相比其他语言来说更加轻量,依赖非常少。xmake还有一些其他方面的功能</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Xmake ≈ Make/Ninja + CMake/Meson + Vcpkg/Conan + distcc + ccache/sccache </code></pre></div></div>
<blockquote> <p>https://gitee.com/tboox/xmake</p> </blockquote>
<p>同样的国外也有人觉得脚本语法难用,所以他用python也做了一套,叫Meson,现在基本上人手一个python,你要是不会python,多少都有点奇怪。</p>
<blockquote> <p>https://mesonbuild.com/</p>
<p>https://github.com/mesonbuild/meson</p> </blockquote>
<p>虽然用python简单,但是python本身稍微有点重,还是不够好</p>
<p>SCons也做了类似的事情,使用python来写</p>
<blockquote> <p>https://scons.org/</p> </blockquote>
<p>作为工具链的头头GNU也有一套构建工具GNU Autotools,但是对于其他平台的支持就不太行了</p>
<h2 id="xmake">xmake</h2>
<p>试用一下xmake,主要是看看他的menu图形化是怎么做的,文档里也没有详细说明</p>
<h3 id="部署">部署</h3>
<p>安装xmake,windows直接下载exe版本即可</p>
<blockquote> <p>https://github.com/xmake-io/xmake/releases</p> </blockquote>
<h2 id="测试">测试</h2>
<h4 id="hello">hello</h4>
<p>拉下来xmake的代码,测试工程都在test/project中</p>
<blockquote> <p>https://github.com/xmake-io/xmake</p> </blockquote>
<p>创建一个hello工程</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake create -l c -P ./hello </code></pre></div></div>
<p>如果正常的话,可以直接xmake,但是实际可能不行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake </code></pre></div></div>
<p>xmake会自动检测设备环境,自动关联查找各种库,由于默认的xmake.lua中没有设定环境,所以导致实际上自动找的工具链大概率是错的,编译过不去</p>
<p>首先指定环境</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake f <span class="nt">-p</span> windows <span class="nt">-a</span> x64 </code></pre></div></div>
<p>他会自动找到对应安装的环境</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>checking <span class="k">for </span>Microsoft Visual Studio <span class="o">(</span>x64<span class="o">)</span> version … 2022 checking <span class="k">for </span>Microsoft C/C++ Compiler <span class="o">(</span>x64<span class="o">)</span> version … 19.35.32019 </code></pre></div></div>
<p>再次xmake,这次编译成功了</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span> 50%]: compiling.release src<span class="se">\m</span>ain.c <span class="o">[</span> 75%]: linking.release hello.exe <span class="o">[</span>100%]: build ok, spent 1.032s </code></pre></div></div>
<p>运行程序,直接输出hello world</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake run </code></pre></div></div>
<p>再测试一下嵌入式环境,进入到<code class="language-plaintext highlighter-rouge">xmake\tests\projects\embed\mdk\hello\</code>路径中</p>
<p>嵌入式环境比较复杂,xmake都算作交叉编译,参考这部分文档</p>
<ul> <li>-p是指定平台,比如交叉编译、windows、linux、mac,这种大平台级别</li> <li>-a 基本可以理解为系统架构,比如x86、x64、cortex-m3、cortex-m4这样的</li> <li>–toolchain 自动搜索对应的工具链,并且使用 <code class="language-plaintext highlighter-rouge">xmake show -l toolchains</code>可以显示当前支持的工具链</li> <li>–sdk指定具体的工具链的库地址,根目录,大部分会自动识别库内结构,找到对应工具</li> </ul>
<p>cross这里默认就是指交叉编译的库</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake f -p cross -a cortex-m3 –toolchain=armcc -c xmake </code></pre></div></div>
<p>armcc的方式可以正常编译过</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake f -p cross -a cortex-m3 –toolchain=armclang -c xmake </code></pre></div></div>
<p>armclang,也就是keil的v6编译,过不去</p>
<p>cross这里默认就是指交叉编译的库,具体sdk路径最好带上引号</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake f -p cross –toolchain=gnu-rm –sdk="D:\GNU Arm Embedded Toolchain\10 2021.10" xmake </code></pre></div></div>
<p>如果遇到这种问题,实际就是你的编译工具链不完整,缺少内容</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>note: the following packages are unsupported on msys/x86_64: -> gnu-rm 2021.10 [host] </code></pre></div></div>
<h4 id="menu">menu</h4>
<p>看似可以一键直接使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xmake f –menu </code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021540966.png" alt="img" /></p>
<p>但实际上不行,这里需要tui支持,windows这边的git bash客户端不支持这个界面,但是如果直接用cmd反而支持tui</p>
<p>xmake.lua中添加下面的几个选型,即可在菜单中增加一些选型,后续可以和实际编译关联在一起</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>– ‘boolean’ option option("test1") set_default(true) set_showmenu(true) set_category("root menu/test1")
– ‘choice’ option with values: "a", "b", "c" option("test2") set_default("a") set_values("a", "b", "c") set_showmenu(true) set_category("root menu/test2")
– ‘string’ option option("test3") set_default("xx") set_showmenu(true) set_category("root menu/test3/test3")
– ‘number’ option option("test4") set_default(6) set_showmenu(true) set_category("root menu/test4") </code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021602539.png" alt="image-20241202160200487" /></p>
<p>实际这个menu中除了用户自定义的这一部分,剩下的选型基本就是xmake的参数配置,可以在menu中一层层配置好,就是操作起来有点麻烦。</p>
<h2 id="summary">Summary</h2>
<p>回归初心,实际如果用类似这样的menu做构建,真的比直接用现成的IDE好了嘛,只能说这样的menu是一种通用的交互手段,可以跨平台服务各种代码,但是对于本身不太需要跨平台的项目来说,menu就是一种倒退。</p>
<p>类似的linux,menu能用起来是建立在完善的配置项上,各种模块之间耦合或者冲突都需要完善到配置文件中,否则别人用起来也会出一堆问题。</p>
<h2 id="quote">Quote</h2>
<blockquote> <p>https://www.cnblogs.com/zxdplay/p/17768181.html</p>
<p>https://www.cnblogs.com/zzb-Dream-90Time/p/7111435.html</p>
<p>https://cloud.tencent.com/developer/article/1825589</p>
<p>https://tboox.org/cn/2018/02/03/update-v2.1.9/</p> </blockquote>
</description>
<category>build</category>
</item>
<item>
<title>Python gRPC</title>
<description><h2 id="foreword">Foreword</h2>
<h2 id="grpc">gRPC</h2>
<h3 id="example测试">example测试</h3>
<p>安装gRPC库</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install grpcio </code></pre></div></div>
<p>安装gRPC工具</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip install grpcio-tools </code></pre></div></div>
<p>下载官方例程</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone -b v1.66.0 –depth 1 –shallow-submodules https://github.com/grpc/grpc </code></pre></div></div>
<p>演示用例在这里</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grpc/examples/python/helloworld </code></pre></div></div>
<p>先启动服务端</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python greeter_server.py </code></pre></div></div>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411201710588.png" alt="image-20241120171002516" /></p>
<p>可以看到已经在监听了</p>
<p>再启动客户端</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python greeter_client.py </code></pre></div></div>
<p>正常连接到了服务端</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411201710582.png" alt="image-20241120171021552" /></p>
<h3 id="源码分析">源码分析</h3>
<p>服务端</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">concurrent</span> <span class="kn">import</span> <span class="n">futures</span> <span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">grpc</span> <span class="kn">import</span> <span class="nn">helloworld_pb2</span> <span class="kn">import</span> <span class="nn">helloworld_pb2_grpc</span>
<span class="c1"># 继承自helloworld_pb2_grpc.GreeterServicer,重写了sayhello的函数 </span><span class="k">class</span> <span class="nc">Greeter</span><span class="p">(</span><span class="n">helloworld_pb2_grpc</span><span class="p">.</span><span class="n">GreeterServicer</span><span class="p">):</span> <span class="k">def</span> <span class="nf">SayHello</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span> <span class="c1"># 对应返回 hello 和访问者的名字 </span> <span class="k">return</span> <span class="n">helloworld_pb2</span><span class="p">.</span><span class="n">HelloReply</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s">"Hello, %s!"</span> <span class="o">%</span> <span class="n">request</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">serve</span><span class="p">():</span> <span class="c1"># 启动还是比较简单的,设置好端口 </span> <span class="n">port</span> <span class="o">=</span> <span class="s">"50051"</span> <span class="c1"># 调用helloworld_pb2_grpc就完成了 </span> <span class="n">server</span> <span class="o">=</span> <span class="n">grpc</span><span class="p">.</span><span class="n">server</span><span class="p">(</span><span class="n">futures</span><span class="p">.</span><span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="mi">10</span><span class="p">))</span> <span class="n">helloworld_pb2_grpc</span><span class="p">.</span><span class="n">add_GreeterServicer_to_server</span><span class="p">(</span><span class="n">Greeter</span><span class="p">(),</span> <span class="n">server</span><span class="p">)</span> <span class="n">server</span><span class="p">.</span><span class="n">add_insecure_port</span><span class="p">(</span><span class="s">"[::]:"</span> <span class="o">+</span> <span class="n">port</span><span class="p">)</span> <span class="n">server</span><span class="p">.</span><span class="n">start</span><span class="p">()</span> <span class="k">print</span><span class="p">(</span><span class="s">"Server started, listening on "</span> <span class="o">+</span> <span class="n">port</span><span class="p">)</span> <span class="n">server</span><span class="p">.</span><span class="n">wait_for_termination</span><span class="p">()</span>
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">"main"</span><span class="p">:</span> <span class="n">logging</span><span class="p">.</span><span class="n">basicConfig</span><span class="p">()</span> <span class="n">serve</span><span class="p">()</span>
</code></pre></div></div>
<p>客户端</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">future</span> <span class="kn">import</span> <span class="n">print_function</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">import</span> <span class="nn">grpc</span> <span class="kn">import</span> <span class="nn">helloworld_pb2</span> <span class="kn">import</span> <span class="nn">helloworld_pb2_grpc</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">():</span> <span class="c1"># NOTE(gRPC Python Team): .close() is possible on a channel and should be </span> <span class="c1"># used in circumstances in which the with statement does not fit the needs </span> <span class="c1"># of the code. </span> <span class="k">print</span><span class="p">(</span><span class="s">"Will try to greet world …"</span><span class="p">)</span> <span class="c1"># 设置本地 端口 </span> <span class="k">with</span> <span class="n">grpc</span><span class="p">.</span><span class="n">insecure_channel</span><span class="p">(</span><span class="s">"localhost:50051"</span><span class="p">)</span> <span class="k">as</span> <span class="n">channel</span><span class="p">:</span> <span class="n">stub</span> <span class="o">=</span> <span class="n">helloworld_pb2_grpc</span><span class="p">.</span><span class="n">GreeterStub</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span> <span class="c1"># 发送信息 并等待结果 </span> <span class="n">response</span> <span class="o">=</span> <span class="n">stub</span><span class="p">.</span><span class="n">SayHello</span><span class="p">(</span><span class="n">helloworld_pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"you"</span><span class="p">))</span> <span class="k">print</span><span class="p">(</span><span class="s">"Greeter client received: "</span> <span class="o">+</span> <span class="n">response</span><span class="p">.</span><span class="n">message</span><span class="p">)</span>
<span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">"main"</span><span class="p">:</span> <span class="n">logging</span><span class="p">.</span><span class="n">basicConfig</span><span class="p">()</span> <span class="n">run</span><span class="p">()</span>
</code></pre></div></div>
<p>helloworld_pb2_grpc.py</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 继承的原型函数在这里 </span><span class="k">class</span> <span class="nc">GreeterServicer</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span> <span class="s">"""The greeting service definition. """</span>
<span class="k">def</span> <span class="nf">SayHello</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
<span class="s">"""Sends a greeting
"""</span>
<span class="n">context</span><span class="p">.</span><span class="n">set_code</span><span class="p">(</span><span class="n">grpc</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">.</span><span class="n">UNIMPLEMENTED</span><span class="p">)</span>
<span class="n">context</span><span class="p">.</span><span class="n">set_details</span><span class="p">(</span><span class="s">'Method not implemented!'</span><span class="p">)</span>
<span class="k">raise</span> <span class="nb">NotImplementedError</span><span class="p">(</span><span class="s">'Method not implemented!'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">SayHelloStreamReply</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
<span class="s">"""Missing associated documentation comment in .proto file."""</span>
<span class="n">context</span><span class="p">.</span><span class="n">set_code</span><span class="p">(</span><span class="n">grpc</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">.</span><span class="n">UNIMPLEMENTED</span><span class="p">)</span>
<span class="n">context</span><span class="p">.</span><span class="n">set_details</span><span class="p">(</span><span class="s">'Method not implemented!'</span><span class="p">)</span>
<span class="k">raise</span> <span class="nb">NotImplementedError</span><span class="p">(</span><span class="s">'Method not implemented!'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">SayHelloBidiStream</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request_iterator</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
<span class="s">"""Missing associated documentation comment in .proto file."""</span>
<span class="n">context</span><span class="p">.</span><span class="n">set_code</span><span class="p">(</span><span class="n">grpc</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">.</span><span class="n">UNIMPLEMENTED</span><span class="p">)</span>
<span class="n">context</span><span class="p">.</span><span class="n">set_details</span><span class="p">(</span><span class="s">'Method not implemented!'</span><span class="p">)</span>
<span class="k">raise</span> <span class="nb">NotImplementedError</span><span class="p">(</span><span class="s">'Method not implemented!'</span><span class="p">)</span>
<span class="c1"># 主要是这个函数,把函数的返回绑定到一起 </span><span class="k">def</span> <span class="nf">add_GreeterServicer_to_server</span><span class="p">(</span><span class="n">servicer</span><span class="p">,</span> <span class="n">server</span><span class="p">):</span> <span class="n">rpc_method_handlers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">‘SayHello’</span><span class="p">:</span> <span class="n">grpc</span><span class="p">.</span><span class="n">unary_unary_rpc_method_handler</span><span class="p">(</span> <span class="n">servicer</span><span class="p">.</span><span class="n">SayHello</span><span class="p">,</span> <span class="n">request_deserializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">.</span><span class="n">FromString</span><span class="p">,</span> <span class="n">response_serializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloReply</span><span class="p">.</span><span class="n">SerializeToString</span><span class="p">,</span> <span class="p">),</span> <span class="s">‘SayHelloStreamReply’</span><span class="p">:</span> <span class="n">grpc</span><span class="p">.</span><span class="n">unary_stream_rpc_method_handler</span><span class="p">(</span> <span class="n">servicer</span><span class="p">.</span><span class="n">SayHelloStreamReply</span><span class="p">,</span> <span class="n">request_deserializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">.</span><span class="n">FromString</span><span class="p">,</span> <span class="n">response_serializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloReply</span><span class="p">.</span><span class="n">SerializeToString</span><span class="p">,</span> <span class="p">),</span> <span class="s">‘SayHelloBidiStream’</span><span class="p">:</span> <span class="n">grpc</span><span class="p">.</span><span class="n">stream_stream_rpc_method_handler</span><span class="p">(</span> <span class="n">servicer</span><span class="p">.</span><span class="n">SayHelloBidiStream</span><span class="p">,</span> <span class="n">request_deserializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">.</span><span class="n">FromString</span><span class="p">,</span> <span class="n">response_serializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloReply</span><span class="p">.</span><span class="n">SerializeToString</span><span class="p">,</span> <span class="p">),</span> <span class="p">}</span> <span class="c1"># 创建服务名称和通用句柄 </span> <span class="n">generic_handler</span> <span class="o">=</span> <span class="n">grpc</span><span class="p">.</span><span class="n">method_handlers_generic_handler</span><span class="p">(</span> <span class="s">‘helloworld.Greeter’</span><span class="p">,</span> <span class="n">rpc_method_handlers</span><span class="p">)</span> <span class="c1"># server添加通用的句柄 </span> <span class="n">server</span><span class="p">.</span><span class="n">add_generic_rpc_handlers</span><span class="p">((</span><span class="n">generic_handler</span><span class="p">,))</span> <span class="c1"># 将处理方法注册给server </span> <span class="n">server</span><span class="p">.</span><span class="n">add_registered_method_handlers</span><span class="p">(</span><span class="s">‘helloworld.Greeter’</span><span class="p">,</span> <span class="n">rpc_method_handlers</span><span class="p">)</span> </code></pre></div></div>
<p>helloworld_pb2.py</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">"""Generated protocol buffer code."""</span> <span class="kn">from</span> <span class="nn">google.protobuf</span> <span class="kn">import</span> <span class="n">descriptor</span> <span class="k">as</span> <span class="n">_descriptor</span> <span class="kn">from</span> <span class="nn">google.protobuf</span> <span class="kn">import</span> <span class="n">descriptor_pool</span> <span class="k">as</span> <span class="n">_descriptor_pool</span> <span class="kn">from</span> <span class="nn">google.protobuf</span> <span class="kn">import</span> <span class="n">runtime_version</span> <span class="k">as</span> <span class="n">_runtime_version</span> <span class="kn">from</span> <span class="nn">google.protobuf</span> <span class="kn">import</span> <span class="n">symbol_database</span> <span class="k">as</span> <span class="n">_symbol_database</span> <span class="kn">from</span> <span class="nn">google.protobuf.internal</span> <span class="kn">import</span> <span class="n">builder</span> <span class="k">as</span> <span class="n">_builder</span> <span class="n">_runtime_version</span><span class="p">.</span><span class="n">ValidateProtobufRuntimeVersion</span><span class="p">(</span> <span class="n">_runtime_version</span><span class="p">.</span><span class="n">Domain</span><span class="p">.</span><span class="n">PUBLIC</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">27</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="s">’‘</span><span class="p">,</span> <span class="s">‘helloworld.proto’</span> <span class="p">)</span> <span class="c1"># @@protoc_insertion_point(imports) </span> <span class="n">_sym_db</span> <span class="o">=</span> <span class="n">_symbol_database</span><span class="p">.</span><span class="n">Default</span><span class="p">()</span>
<span class="c1"># 这里直接用代码的形式写了一个helloworld的描述符 </span><span class="n">DESCRIPTOR</span> <span class="o">=</span> <span class="n">_descriptor_pool</span><span class="p">.</span><span class="n">Default</span><span class="p">().</span><span class="n">AddSerializedFile</span><span class="p">(</span><span class="s">b’</span><span class="se">\n\x10</span><span class="s">helloworld.proto</span><span class="se">\x12\n</span><span class="s">helloworld</span><span class="se">\"\x1c\n\x0c</span><span class="s">HelloRequest</span><span class="se">\x12\x0c\n\x04</span><span class="s">name</span><span class="se">\x18\x01</span><span class="s"> </span><span class="se">\x01</span><span class="s">(</span><span class="se">\t\"\x1d\n\n</span><span class="s">HelloReply</span><span class="se">\x12\x0f\n\x07</span><span class="s">message</span><span class="se">\x18\x01</span><span class="s"> </span><span class="se">\x01</span><span class="s">(</span><span class="se">\t</span><span class="s">2</span><span class="se">\xe4\x01\n\x07</span><span class="s">Greeter</span><span class="se">\x12</span><span class="s">></span><span class="se">\n\x08</span><span class="s">SayHello</span><span class="se">\x12\x18</span><span class="s">.helloworld.HelloRequest</span><span class="se">\x1a\x16</span><span class="s">.helloworld.HelloReply</span><span class="se">\"\x00\x12</span><span class="s">K</span><span class="se">\n\x13</span><span class="s">SayHelloStreamReply</span><span class="se">\x12\x18</span><span class="s">.helloworld.HelloRequest</span><span class="se">\x1a\x16</span><span class="s">.helloworld.HelloReply</span><span class="se">\"\x00\x30\x01\x12</span><span class="s">L</span><span class="se">\n\x12</span><span class="s">SayHelloBidiStream</span><span class="se">\x12\x18</span><span class="s">.helloworld.HelloRequest</span><span class="se">\x1a\x16</span><span class="s">.helloworld.HelloReply</span><span class="se">\"\x00</span><span class="s">(</span><span class="se">\x01\x30\x01\x42\x36\n\x1b</span><span class="s">io.grpc.examples.helloworldB</span><span class="se">\x0f</span><span class="s">HelloWorldProtoP</span><span class="se">\x01\xa2\x02\x03</span><span class="s">HLWb</span><span class="se">\x06</span><span class="s">proto3’</span><span class="p">)</span>
<span class="n">_globals</span> <span class="o">=</span> <span class="nb">globals</span><span class="p">()</span> <span class="n">_builder</span><span class="p">.</span><span class="n">BuildMessageAndEnumDescriptors</span><span class="p">(</span><span class="n">DESCRIPTOR</span><span class="p">,</span> <span class="n">_globals</span><span class="p">)</span> <span class="n">_builder</span><span class="p">.</span><span class="n">BuildTopDescriptorsAndMessages</span><span class="p">(</span><span class="n">DESCRIPTOR</span><span class="p">,</span> <span class="s">‘helloworld_pb2’</span><span class="p">,</span> <span class="n">_globals</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">_descriptor</span><span class="p">.</span><span class="n">_USE_C_DESCRIPTORS</span><span class="p">:</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘DESCRIPTOR’</span><span class="p">].</span><span class="n">_loaded_options</span> <span class="o">=</span> <span class="bp">None</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘DESCRIPTOR’</span><span class="p">].</span><span class="n">_serialized_options</span> <span class="o">=</span> <span class="s">b’</span><span class="se">\n\033</span><span class="s">io.grpc.examples.helloworldB</span><span class="se">\017</span><span class="s">HelloWorldProtoP</span><span class="se">\001\242\002\003</span><span class="s">HLW’</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘_HELLOREQUEST’</span><span class="p">].</span><span class="n">_serialized_start</span><span class="o">=</span><span class="mi">32</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘_HELLOREQUEST’</span><span class="p">].</span><span class="n">_serialized_end</span><span class="o">=</span><span class="mi">60</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘_HELLOREPLY’</span><span class="p">].</span><span class="n">_serialized_start</span><span class="o">=</span><span class="mi">62</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘_HELLOREPLY’</span><span class="p">].</span><span class="n">_serialized_end</span><span class="o">=</span><span class="mi">91</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘_GREETER’</span><span class="p">].</span><span class="n">_serialized_start</span><span class="o">=</span><span class="mi">94</span> <span class="n">_globals</span><span class="p">[</span><span class="s">‘_GREETER’</span><span class="p">].</span><span class="n">_serialized_end</span><span class="o">=</span><span class="mi">322</span> <span class="c1"># @@protoc_insertion_point(module_scope) </span> </code></pre></div></div>
<p>实际这里使用的proto文件,是如下定义的</p>
<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// The greeting service definition.</span> <span class="kd">service</span> <span class="n">Greeter</span> <span class="p">{</span> <span class="c1">// Sends a greeting</span> <span class="k">rpc</span> <span class="n">SayHello</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span> <span class="p">}</span>
<span class="c1">// The request message containing the user’s name.</span> <span class="kd">message</span> <span class="nc">HelloRequest</span> <span class="p">{</span> <span class="kt">string</span> <span class="na">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// The response message containing the greetings</span> <span class="kd">message</span> <span class="nc">HelloReply</span> <span class="p">{</span> <span class="kt">string</span> <span class="kd">message</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div>
<h3 id="自定义函数">自定义函数</h3>
<p>实际使用的proto文件是在<code class="language-plaintext highlighter-rouge">examples/protos/helloworld.proto</code>中的,这里添加一个新的函数</p>
<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">syntax</span> <span class="o">=</span> <span class="s">"proto3"</span><span class="p">;</span>
<span class="k">option</span> <span class="na">java_multiple_files</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="k">option</span> <span class="na">java_package</span> <span class="o">=</span> <span class="s">"io.grpc.examples.helloworld"</span><span class="p">;</span> <span class="k">option</span> <span class="na">java_outer_classname</span> <span class="o">=</span> <span class="s">"HelloWorldProto"</span><span class="p">;</span> <span class="k">option</span> <span class="na">objc_class_prefix</span> <span class="o">=</span> <span class="s">"HLW"</span><span class="p">;</span>
<span class="kn">package</span> <span class="nn">helloworld</span><span class="p">;</span>
<span class="c1">// The greeting service definition.</span> <span class="kd">service</span> <span class="n">Greeter</span> <span class="p">{</span> <span class="c1">// Sends a greeting</span> <span class="k">rpc</span> <span class="n">SayHello</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">rpc</span> <span class="n">SayHello2</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">rpc</span> <span class="n">SayHelloStreamReply</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">stream</span> <span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">rpc</span> <span class="n">SayHelloBidiStream</span> <span class="p">(</span><span class="n">stream</span> <span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">stream</span> <span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span> <span class="p">}</span>
<span class="c1">// The request message containing the user’s name.</span> <span class="kd">message</span> <span class="nc">HelloRequest</span> <span class="p">{</span> <span class="kt">string</span> <span class="na">name</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>
<span class="c1">// The response message containing the greetings</span> <span class="kd">message</span> <span class="nc">HelloReply</span> <span class="p">{</span> <span class="kt">string</span> <span class="kd">message</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="p">}</span>
</code></pre></div></div>
<p>需要重新生成对应的代码</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> grpc_tools.protoc <span class="nt">-I</span>../../protos <span class="nt">–python_out</span><span class="o">=</span><span class="nb">.</span> <span class="nt">–pyi_out</span><span class="o">=</span><span class="nb">.</span> <span class="nt">–grpc_python_out</span><span class="o">=</span><span class="nb">.</span> ../../protos/helloworld.proto </code></pre></div></div>
<p>这里就会重新生成了</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">add_GreeterServicer_to_server</span><span class="p">(</span><span class="n">servicer</span><span class="p">,</span> <span class="n">server</span><span class="p">):</span> <span class="n">rpc_method_handlers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">‘SayHello’</span><span class="p">:</span> <span class="n">grpc</span><span class="p">.</span><span class="n">unary_unary_rpc_method_handler</span><span class="p">(</span> <span class="n">servicer</span><span class="p">.</span><span class="n">SayHello</span><span class="p">,</span> <span class="n">request_deserializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">.</span><span class="n">FromString</span><span class="p">,</span> <span class="n">response_serializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloReply</span><span class="p">.</span><span class="n">SerializeToString</span><span class="p">,</span> <span class="p">),</span> <span class="s">‘SayHello2’</span><span class="p">:</span> <span class="n">grpc</span><span class="p">.</span><span class="n">unary_unary_rpc_method_handler</span><span class="p">(</span> <span class="n">servicer</span><span class="p">.</span><span class="n">SayHello2</span><span class="p">,</span> <span class="n">request_deserializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">.</span><span class="n">FromString</span><span class="p">,</span> <span class="n">response_serializer</span><span class="o">=</span><span class="n">helloworld__pb2</span><span class="p">.</span><span class="n">HelloReply</span><span class="p">.</span><span class="n">SerializeToString</span><span class="p">,</span> <span class="p">),</span> </code></pre></div></div>
<p>server和client都增加hello2以后,再次运行就能看到已经给过来正确的反应了</p>
<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202411221447146.png" alt="image-20241122144701070" /></p>
<p>到这里最简单的gRPC就完成了</p>
<h3 id="小总结">小总结</h3>
<p>核心就三步:</p>
<ol> <li>定义proto,其实就是定义函数和参数,</li> <li>生成,生成会自动根据定义,生成中间需要的类或者成员函数</li> <li>修改server和client的调用</li> </ol>
<h2 id="grpc的流式传输">gRPC的流式传输</h2>
<p>上面演示的例子都是C/S架构的,也是gRPC常用的模式,一方请求,一方应答,这是普通的RPC。服务方是不能主动发起请求的,只有客户方主动。还有其他3种方式。</p>
<ul> <li>响应流式传输</li> <li>请求流式传输</li> <li>双向流式传输</li> </ul>
<p>还是之前的例子中,有对应的流式实现</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">NUMBER_OF_REPLY</span> <span class="o">=</span> <span class="mi">10</span> <span class="k">class</span> <span class="nc">Greeter</span><span class="p">(</span><span class="n">MultiGreeterServicer</span><span class="p">):</span> <span class="k">async</span> <span class="k">def</span> <span class="nf">sayHello</span><span class="p">(</span> <span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">:</span> <span class="n">HelloRequest</span><span class="p">,</span> <span class="n">context</span><span class="p">:</span> <span class="n">grpc</span><span class="p">.</span><span class="n">aio</span><span class="p">.</span><span class="n">ServicerContext</span> <span class="p">)</span> <span class="o">-></span> <span class="n">HelloReply</span><span class="p">:</span> <span class="n">logging</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">"Serving sayHello request %s"</span><span class="p">,</span> <span class="n">request</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">NUMBER_OF_REPLY</span><span class="p">):</span> <span class="k">yield</span> <span class="n">HelloReply</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s">f"Hello number </span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">, </span><span class="si">{</span><span class="n">request</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s">!"</span><span class="p">)</span>
</code></pre></div></div>
<p>对于服务端的响应,这里可以看到返回了10此请求,并且这个函数是异步的</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">def</span> <span class="nf">run</span><span class="p">()</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span> <span class="k">async</span> <span class="k">with</span> <span class="n">grpc</span><span class="p">.</span><span class="n">aio</span><span class="p">.</span><span class="n">insecure_channel</span><span class="p">(</span><span class="s">"localhost:50051"</span><span class="p">)</span> <span class="k">as</span> <span class="n">channel</span><span class="p">:</span> <span class="n">stub</span> <span class="o">=</span> <span class="n">hellostreamingworld_pb2_grpc</span><span class="p">.</span><span class="n">MultiGreeterStub</span><span class="p">(</span><span class="n">channel</span><span class="p">)</span>
<span class="c1"># Read from an async generator </span> <span class="k">async</span> <span class="k">for</span> <span class="n">response</span> <span class="ow">in</span> <span class="n">stub</span><span class="p">.</span><span class="n">sayHello</span><span class="p">(</span>
<span class="n">hellostreamingworld_pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"you"</span><span class="p">)</span>
<span class="p">):</span>
<span class="k">print</span><span class="p">(</span>
<span class="s">"Greeter client received from async generator: "</span>
<span class="o">+</span> <span class="n">response</span><span class="p">.</span><span class="n">message</span>
<span class="p">)</span>
<span class="c1"># Direct read from the stub </span> <span class="n">hello_stream</span> <span class="o">=</span> <span class="n">stub</span><span class="p">.</span><span class="n">sayHello</span><span class="p">(</span>
<span class="n">hellostreamingworld_pb2</span><span class="p">.</span><span class="n">HelloRequest</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"you"</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">response</span> <span class="o">=</span> <span class="k">await</span> <span class="n">hello_stream</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">if</span> <span class="n">response</span> <span class="o">==</span> <span class="n">grpc</span><span class="p">.</span><span class="n">aio</span><span class="p">.</span><span class="n">EOF</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">print</span><span class="p">(</span>
<span class="s">"Greeter client received from direct read: "</span> <span class="o">+</span> <span class="n">response</span><span class="p">.</span><span class="n">message</span>
<span class="p">)</span> </code></pre></div></div>
<p>客户端这边,前面是异步流式获取,后面是正常流式获取获取</p>
<p>可能看例子,这里流式传输的意义不是很明显,除了能多次发送请求或者多次响应,还有啥用。</p>
<ul> <li>大文件流式传输就需要多次请求和多次响应,比如音频、视频</li> <li>主动推送或者回报就需要流式来实现,比如广告、广播推送</li> <li>高并发,可以同时响应多个请求,不再是顺序执行,串联影响效率</li> <li>任务完成的进度回显就必须多次响应</li> </ul>
<p>grpc的双向流式可以类比成WebSocket,客户端和服务器都可以互相发送信息</p>
<div class="language-protobuf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// The greeting service definition.</span> <span class="kd">service</span> <span class="n">Greeter</span> <span class="p">{</span> <span class="c1">// Sends a greeting</span> <span class="k">rpc</span> <span class="n">SayHello</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">rpc</span> <span class="n">SayHello2</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">rpc</span> <span class="n">SayHelloStreamReply</span> <span class="p">(</span><span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">stream</span> <span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">rpc</span> <span class="n">SayHelloBidiStream</span> <span class="p">(</span><span class="n">stream</span> <span class="n">HelloRequest</span><span class="p">)</span> <span class="k">returns</span> <span class="p">(</span><span class="n">stream</span> <span class="n">HelloReply</span><span class="p">)</span> <span class="p">{}</span> <span class="p">}</span> </code></pre></div></div>
<p>在流式传输的例子中proto文件的定义使用了一个特殊关键词,<code class="language-plaintext highlighter-rouge">stream</code>凡是被stream修饰的参数,那么传输时就会采用流式。</p>
<p>如果修饰到返回值,那就是服务器流式,如果修饰参数,那就是客户端流式,如果同时有那就是双向流式传输</p>
<h2 id="summary">Summary</h2>
<p>总体来说gRPC就是这样,流式上感觉似乎没有WebSocket简单,特别是如果是用来做双向交互的时候WebSocket似乎更简单,更好做一些</p>
<h2 id="quote">Quote</h2>
<blockquote> <p>https://grpc.io/docs/languages/python/quickstart/</p>
<p>https://blog.yuanpei.me/posts/grpc-streaming-transmission-minimalist-guide/</p>
<p>https://hamhire.tech/posts/coding/grpc-03.stream-demo.html</p> </blockquote>
</description>
<category>gRPC</category>
</item>
</channel> </rss>
```
认证完成以后就会有一个小勾了
分享
这是我多年积攒下来的一些订阅源,直接用Follow就可以分享,甚至还可以出售
https://app.follow.is/share/lists/104444993002142720
看起来似乎Follow想通过这种订阅或者说活跃转化成对应的虚拟货币进而维持或者是激励??
这里是我自己的归类,似乎和上面的一样,只是订阅这个人可以订阅到他设置的所有订阅源?
https://app.follow.is/share/users/101522814280570880
看了下RSSHub,在这里竟然是有人提供RSSHub实例然后收费,帮你爬取一些内容,普通人部署了RSSHub也可以加入其中,目前官方应该是免费给你用的,可能某一天官方就不免费了,就要你自己去爬取内容了?那我为什么不用TTRSS,直接全集成好了,还不要去给别人掏钱,费这个鸟劲。
Summary
目前Follow已经是公测阶段,可以免费加入了,但是功能不是完全体的,要完全功能还是得邀请码激活,这个比较麻烦,我也是问人要了一个才能导入甚至分享订阅的
目前看Follow有点意思,但是不多,比我的TTRSS其实没好多少,很多页面也是完全爬不到,还得配合RSSHub来一起使用抓取内容,那我感觉就没啥必要了,不能直接整合这个东西,体验上还是差一点的
目前看起来这个生态只是个金字塔,只有顶端流量大的人有的赚,其他人只能被收割,看起来并不是一个能循环起来的东西。前一段时间的火爆,现在很少看到相关内容了,估计后续生态起不来依然会沉寂到死
他只能对现有RSS的订阅再次划分,而不能产生新的内容,甚至想依靠这种划分来收割一波,信息差这么好利用吗,而至于你订阅源是否能得到利益,那就很难说了,订阅源作为核心的产出者,没有得到应有的流量,反而把利润分给了分发者和服务部署的人。生产者和消费者被剥削了,中介从中获利?
Quote
https://app.follow.is/share/lists/104444993002142720