ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Tutanota IPC 跨平台通信编译器 licc 详解:从 JSON Schema 到多语言代码自动生成

Tutanota IPC 跨平台通信编译器 licc 详解:从 JSON Schema 到多语言代码自动生成 协同办公密码学【免费下载链接】tutanotaTuta is an email service with a strong focus on security and privacy that lets you encrypt emails, contacts and calendar entries on all your devices.项目地址https://gitcode.com/gh_mirrors/tu/tutanota点击查看免费下载在跨平台应用的开发中如何优雅地实现不同语言、不同运行时之间的进程间通信IPC是一个核心挑战。Tutanota 项目提供了一套精巧的自研工具 ——liccLittle InterProcess Communications Compiler通过一份 JSON5 Schema 定义文件自动生成 TypeScript、KotlinAndroid、SwiftiOS三种语言的类型定义与双向调度Dispatch骨架代码。本文基于仓库文档及完全开源实现的源码系统讲解 licc 的工作原理、Schema 语法、多语言代码生成机制及实际使用方式。什么是 licclicclittle interprocess communications compiler是 Tutanota 项目专用的 IPC 代码生成器。它的核心工作是将一系列按约定格式编写的 JSON5 定义文件编译为指定平台web / desktop / android / ios对应的源文件覆盖数据类型定义struct、enum、typeref和远程方法调用facade的收发两端调度器代码。licc 的全部源码位于 buildSrc/licc/可通过licc --help获取命令行帮助信息。该工具的输入源自项目中的 ipc-schema/ 目录其中包含了所有的 facade 定义和类型定义文件。快速上手命令行用法licc 作为 Node.js CLI 工具运行入口文件为 buildSrc/licc/cli.ts。其基本调用方式为licc [options] from_dir to_dirfrom_dir包含 JSON / JSON5 定义文件的输入目录。to_dir生成的代码输出目录。-p, --platform platform指定目标平台可选值为ios、web、desktop、android。当不指定-p时from_dir下必须存在.liccc配置文件以 JSON 格式记录各平台到输出子目录的映射。例如从ipc-schema/编译所有定义为web平台生成代码licc -p web ipc-schema/ ./out/web若不传-p则在ipc-schema/目录下放置.liccc文件内容示例{ web: out/web, android: out/android, ios: out/ios }然后直接运行licc ipc-schema/licc 会递归扫描from_dir中的所有.json和.json5文件依据每个文件的顶层type属性执行对应代码生成逻辑将生成的文件写入各平台指定的输出目录。定义文件类型与语法licc 通过 JSON5 文件的顶层type属性区分四种定义类型struct、enum、facade和typeref。完整类型定义可在 buildSrc/licc/common.ts 中找到对应DefinationType枚举及StructDefinition、FacadeDefinition、TypeRefDefinition、EnumDefinition接口。struct结构体type: struct用于定义数据传输的纯数据对象。字段以fieldName: fieldType的键值对形式声明。{ name: CredentialsInfo, type: struct, doc: Key definition for shortcuts., fields: { login: string, userId: string, type: CredentialType } }该定义被生成器解析为语言对应的结构体/类/接口。例如Kotlin 端会生成Serializable data classSwift 端会生成Codable structTypeScript 端会生成export interface。真实的仓库示例位于 ipc-schema/types/CredentialsInfo.json。enum枚举type: enum定义枚举类型通过values数组列出所有枚举值。{ name: CredentialType, type: enum, doc: Type of stored credential, values: [ Internal, External ] }licc 会按平台惯例生成枚举TypeScript 生成const enum值用下标字符串如0、1Kotlin 生成enum class含SerialName注解和fromValue工厂方法Swift 生成String原始值的enum。facade外观接口facade 是 licc 中最复杂、最具价值的定义类型。它声明了一个跨平台的远程方法调用RPC接口明确标识出发起方senders和接收方receivers所在的平台。{ name: SqlCipherFacade, type: facade, senders: [web], receivers: [desktop, android, ios], doc: Operations for encrypted SQLite database via sqlcipher., methods: { openDb: { arg: [ { userId: string }, { dbKey: bytes } ], ret: void }, get: { doc: get a single object or null if the query returns nothing, arg: [ { query: string }, { params: ListTaggedSqlValue } ], ret: Mapstring, TaggedSqlValue? }, run: { arg: [ { query: string }, { params: ListTaggedSqlValue } ], ret: void } } }如 ipc-schema/facades/SqlCipherFacade.json 所示这是一个由 Web 端发起、桌面端和移动端实现的数据库操作接口。每个方法的arg是一个单属性对象列表用于保持参数顺序参见 buildSrc/licc/common.ts#L102-L110 的getArgs实现ret声明返回值类型。typeref类型引用type: typeref用于引用不由 licc 生成、但 facade 定义中需要使用的类型。定义中需指明各语言的引用方式。{ name: DataFile, type: typeref, location: { typescript: tutao/entities/tutanota, kotlin: de.tutao.tutanota.DataFile } }文件 ipc-schema/types/DataFile.json 展示了典型用法。TypeScript 通过模块导入路径引用Kotlin 通过完整包名以typealias引用Swift 端目前返回null需要手动处理。类型系统支持的字段类型licc 的类型解析实现在 buildSrc/licc/Parser.ts 的parseType函数中。支持的类型体系如下写法含义示例string字符串name: stringnumber数字count: numberboolean布尔值visible: booleanbytes二进制数据dbKey: bytesvoid无返回值ret: voidTypeName?可空类型path: string?ListT列表 / 数组items: ListstringMapK, V映射 / 字典ret: Mapstring, TaggedSqlValue?ExternalType外部类型type: CredentialType以上任意组合嵌套泛型ListListExternal??parseType函数输出一个ParsedType结构参见 buildSrc/licc/Parser.ts#L199-L209包含baseName、generics、nullable、external四个字段。其中external为true表示该类型不是内建基本类型需要在生成的代码中添加对应的 import 导入。类型名必须同时是 Kotlin、Swift、TypeScript 三种语言的有效标识符且不能使用三种语言的任一保留关键字参见 buildSrc/licc/Parser.ts#L2-L146 中FORBIDDEN_IDENTIFIERS集合的定义。消息分发机制与代码生成facade 定义中senders和receivers两个字段驱动了整套代码生成逻辑。代码生成入口在 buildSrc/licc/index.ts 的generate函数中。该函数首先根据目标平台映射到目标语言mapPlatformToLang然后遍历所有输入定义分别处理对于 struct / enum / typeref仅生成类型定义文件。对于 facade依次生成接口定义 发送端调度器SendDispatcher 接收端调度器ReceiveDispatcher最终为接收端所在平台生成一个全局调度器GlobalDispatcher。接口定义接口定义仅声明该 facade 的全部方法签名不含任何实现。三种语言的生成器接口一致定义于 buildSrc/licc/common.ts#L1-L39 的LangGenerator接口中。具体实现分别位于TypescriptGenerator.ts生成export interfaceKotlinGenerator.ts生成interface含suspend修饰SwiftGenerator.ts生成public protocol ... : Sendable发送端调度器SendDispatcher在senders包含当前平台的 facades 上licc 会生成XxxFacadeSendDispatcher类。它持有底层传输NativeInterface的实例将每条方法调用的 facade 名称、方法名称和序列化后的参数打包通过统一的transport.invokeNative(ipc, ...)或sendRequest(ipc, ...)接口发送出去。以 TypeScript 为例生成逻辑在 TypescriptGenerator.ts#L157-L173// 生成的 SendDispatcher 示例 export class SqlCipherFacadeSendDispatcher implements SqlCipherFacade { constructor(private readonly transport: NativeInterface) {} async openDb(userId: string, dbKey: Uint8ArrayArrayBuffer) { return this.transport.invokeNative(ipc, [SqlCipherFacade, openDb, userId, dbKey]) } }Kotlin 和 Swift 的 SendDispatcher 则使用 JSON 序列化/反序列化来编解码参数因为 Java / ObjC 桥接限制了原始类型参见 KotlinGenerator.ts#L164-L196 和 SwiftGenerator.ts#L187-L227。接收端调度器ReceiveDispatcher在receivers包含当前平台的 facades 上licc 生成XxxFacadeReceiveDispatcher类。它接收来自传输层的字符串标识facade 名 方法名 参数数组通过switch或when分发到具体的接口方法实现。以下为生成的 TypeScript ReceiveDispatcher 模式提取自 TypescriptGenerator.ts#L120-L155export class SqlCipherFacadeReceiveDispatcher { constructor(private readonly facade: SqlCipherFacade) {} async dispatch(method: string, arg: Arrayany): Promiseany { switch(method) { case openDb: { const userId: string arg[0] const dbKey: Uint8ArrayArrayBuffer arg[1] return this.facade.openDb(userId, dbKey) } // ... 其他方法的 case } } }全局调度器GlobalDispatcher每个接收端平台生成一个全局调度器负责将收到的请求按 facade 名称进一步分发给对应的 ReceiveDispatcher。例如 WebGlobalDispatcher.ts 已生成文件参见文件顶部的/* generated file, dont edit. */标志export class WebGlobalDispatcher { private readonly commonNativeFacade: CommonNativeFacadeReceiveDispatcher private readonly desktopFacade: DesktopFacadeReceiveDispatcher // ... async dispatch(facadeName: string, methodName: string, args: Arrayany) { switch (facadeName) { case CommonNativeFacade: return this.commonNativeFacade.dispatch(methodName, args) case DesktopFacade: return this.desktopFacade.dispatch(methodName, args) // ... } } }完整消息流程结合以上代码生成结果一条 IPC 消息在 Tutanota 中的完整生命周期为README 原文示意SENDING SIDE: *caller* SendDispatcher *outgoing transport* RECEIVING SIDE: *incoming transport* GlobalDispatcher ReceiveDispatcher *facade implementation*其中标记*的是需要开发者手动实现的部分transport 的实际网络/进程通信逻辑和 facade 的业务逻辑其余均由 licc 自动生成。代码辅助工具Accumulatorlicc 的每个 Generator 内部都依赖 Accumulator 工具类来构建代码文本。它提供了链式调用的line、lines、indent、indented、do等方法来格式化代码并用addImport和finish()自动收集和处理 import 语句最终在所有代码之前插入/* generated file, dont edit. */头部标记。所有生成的代码文件均携带该标记提醒开发者不要直接编辑。类型映射规则licc 会根据目标语言将 Schema 类型名映射为相应的原生类型Schema 类型TypeScriptKotlinSwiftstringstringStringStringnumbernumberLongIntbooleanbooleanBooleanBoolbytesUint8ArrayArrayBufferDataWrapperDataWrappervoidvoidUnitVoidListTReadonlyArrayTListT[T]MapK,VRecordK, VMapK, V[K : V]可空T?T \| nullT?T?这些映射规则分别定义在 TypescriptGenerator.ts#L222-L255 的renderTypescriptType、KotlinGenerator.ts#L267-L300 的renderKotlinType以及 SwiftGenerator.ts#L275-L303 的renderSwiftType。开发现状与已知问题licc 本身是 Tutanota 的构建工具链buildSrc的一部分不是面向公开的通用开源库。README 和源码共同标注了其已知局限bytes类型在移动端生成DataWrapper包装类型因为原生桥无法直接区分二进制数据和普通字符串这种包装要求会泄漏到接口消费者的代码中。struct 定义为所有语言生成不按使用情况过滤。JSON 对象字段顺序未定义理论上连续两次编译可能产生不同的输出顺序实际中尚未观察到。缺乏严格的验证licc 不会检测重复的方法名、参数名也不会对类型语法进行深度校验。Swift 的 typeref 处理直接返回null意味着 Swift 端需要手动处理外部类型引用。针对类型解析仓库提供了完整的单元测试位于 test/tests/licc/ParserTest.ts覆盖了非法标识符检测、基本类型解析、可空类型、List 嵌套、Map 泛型等场景。生成代码的实际使用场景Tutanota 项目中licc 生成的代码被集成到 native-bridge 模块src/app-kit/native-bridge/中。项目的各 IPC Facade 定义均位于 ipc-schema/facades/共 24 个 facade 文件涉及凭据管理NativeCredentialsFacade、加密操作NativeCryptoFacade、文件操作FileFacade、IMAP 同步ImapSyncFacade、日历对接ExternalCalendarFacade等多个功能域。以 MobileFacade 为例它定义了 Android/iOS 端发起的常见操作接口如后退键处理、键盘尺寸变化、应用可见性变化由 Web 端接收并处理。而 CommonNativeFacade 则相反由 Android/iOS/Desktop 发起、Web 端接收用于打开邮件编辑器、设置、日历、联系人等操作。这种双向的 sender/receiver 设计让 Tutanota 的 Web 层在保持纯逻辑的同时能够无缝调用各平台的原生能力。总结licc 是 Tutanota 多端 IPC 通信体系中承上启下的代码生成引擎。通过一份统一的 JSON5 Schema它同时为 TypeScript、Kotlin、Swift 三种语言生成类型定义与收发调度器骨架大幅降低了跨平台接口对齐的维护成本。理解 licc 的工作机制有助于深入掌握 Tutanota 项目前后端通信的设计哲学也可以为其他跨平台应用构建自己的 IPC 层时提供有价值的参考。赞分享协同办公密码学【免费下载链接】tutanotaTuta is an email service with a strong focus on security and privacy that lets you encrypt emails, contacts and calendar entries on all your devices.项目地址https://gitcode.com/gh_mirrors/tu/tutanota点击查看免费下载相关推荐Screenshot-to-code多平台编译系统解析从DSL映射到跨端代码生成Screenshot to code多平台编译系统解析从DSL映射到跨端代码生成 引言设计稿到多端代码的自动化挑战 你是否还在为同一设计稿需要编写HTML、示例工程JSON Crack JSON Schema生成器从数据到Schema的自动化流程JSON Crack JSON Schema生成器从数据到Schema的自动化流程 引言告别手动编写JSON Schema的时代 你是否还在为手动编写JSO前端数据可视化开发工具代码生成器原理ZLT平台自动化代码生成机制详解代码生成器原理ZLT平台自动化代码生成机制详解 在当今快速发展的软件开发领域ZLT微服务平台通过其强大的 代码生成器 功能为企业级应用开发带来了革命性的效后端微服务认证鉴权上一篇Cxx.jl未来路线图解锁Julia与C无缝集成的终极指南下一篇OpenUSD 粒子场高斯椭圆核ParticleFieldKernelGaussianEllipsoidAPI 原理与实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表