ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Goose GDK Maven 构件打包与发布指南:将 UniFFI 生成的 Kotlin/JVM 绑定发布为 `io.github.aaif-goose:gdk`

Goose GDK Maven 构件打包与发布指南:将 UniFFI 生成的 Kotlin/JVM 绑定发布为 `io.github.aaif-goose:gdk` Goose GDK Maven 构件打包与发布指南将 UniFFI 生成的 Kotlin/JVM 绑定发布为io.github.aaif-goose:gdk【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose本指南以仓库 crates/goose-sdk/maven/README.md 为骨架围绕 Goose 项目crates/goose-sdk中把 Rust 核心以 UniFFI 方式暴露给 Kotlin/JVM 生态的maven子工程展开。文章会完整说明io.github.aaif-goose:gdk构件的坐标与版本锁定机制、多平台原生库的 JNA 资源布局以及从本地构建、下游冒烟验证到发布 Maven Central 的全套命令与环境配置并结合 build.gradle.kts、shell 脚本与 Kotlin 源码讲清背后的实现原理。读完本文你将具备在本地构建、验证并发布 Kotlin/JVM 版 Goose GDK 的能力。一、工程定位为什么需要独立的 Maven 构件Goose SDK 的 Rust cratecrates/goose-sdk/Cargo.toml本身是可编译为cdylib/staticlib/rlib的 Rust 库并提供了可选的uniffifeature 与配套的goose-uniffi-bindgen二进制用于为 Python 与 Kotlin 等语言生成进程内绑定。maven 子工程就是这一策略的 JVM 一侧落地产物它把 UniFFI生成的 Kotlin/JVM 绑定重新打包成 Maven 构件io.github.aaif-goose:gdkjar 内同时携带生成的 Kotlin API与原生动态库放在 JNA 平台资源目录下使 JVM 调用方只需声明一个 Maven 依赖即可使用 GDK而无需手工配置原生库路径打包所需的 Kotlin 包命名空间由 uniffi.toml 中package_name io.github.aaif_goose决定。注意构建坐标中的组织名是io.github.aaif-goose连字符而 Kotlin 源码包名是io.github.aaif_goose下划线两者用途不同、不可混淆。maven目录下除 Gradle 构建文件外还包含一类特殊源码目录src/support/kotlin存放仓库手工维护而非 UniFFI 生成的支撑类包括 NativeLibraryLoader.kt、ProviderFlow.kt以及anthropic、databricks、groq、openai等 provider 的 Kotlin 封装后续章节会说明这些文件是如何被并入最终 jar 的。二、构件坐标与版本锁定一次 Cargo.toml 变更处处同步构件坐标与版本的来源在 maven/build.gradle.kts 中一目了然group io.github.aaif-goose version gooseSdkVersion() fun gooseSdkVersion(): String { val cargoToml file(../Cargo.toml).readText() return Regex((?m)^version\\s*\\s*\([^\])\) .find(cargoToml) ?.groupValues ?.get(1) ?: error(Could not find goose-sdk version in ../Cargo.toml) }gooseSdkVersion()在 Gradle 配置阶段直接解析../Cargo.toml即 crates/goose-sdk/Cargo.toml中的version字段因此Maven 构件版本与 Rust crate 版本始终保持 lockstep当前仓库中两者同为0.1.0-alpha.7。这意味着发布流程只需在仓库根统一升版即可不需要为 maven 工程单独维护版本号。mavenPublishing配置块还给出了完整的发布元数据与坐标mavenPublishing { publishToMavenCentral(automaticRelease true) if (providers.gradleProperty(signingInMemoryKey).isPresent) { signAllPublications() } coordinates( groupId io.github.aaif-goose, artifactId gdk, version project.version.toString(), ) pom { name.set(Goose GDK) description.set(Kotlin/JVM bindings for the goose Development Kit (GDK)) // licenses / developers / scm 等 POM 元数据 } }要点可以归纳为最终消费坐标为io.github.aaif-goose:gdk:版本publishToMavenCentral(automaticRelease true)表示发布成功后由插件自动完成 Central 的 release只有当系统属性signingInMemoryKey存在时才启用signAllPublications()即“配置了内存签名密钥才进行 PGP 签名”jar 的 Manifest 中会写入Implementation-Title: Goose GDK与Implementation-Version取自工程版本。依赖与运行要求方面build.gradle.kts以api方式暴露了两个关键依赖net.java.dev.jna:jna:5.14.0负责加载原生库与org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2GDK 异步接口需要并统一将 Kotlin 与 Java 的 target 设为JVM 11。三、jar 内部的多平台原生库布局与 JNA 加载原理README 明确说明jar 中除了生成的 Kotlin API还包含位于 JNA 平台资源目录下的原生库打包支持五种前缀JNA 资源前缀对应平台darwin-aarch64macOSApple Silicondarwin-x86-64macOSIntellinux-x86-64Linux x86_64linux-aarch64Linux ARM64win32-x86-64Windows x86_64单次本地打包只会放入当前构建机对应的一种前缀目录README 明确指出把每种原生库都组装进最终发布 jar 的工作由CI负责即 CI 会在各个平台上分别执行打包并把产物聚合。3.1 平台前缀的推导脚本当前平台对应哪个前缀由 maven-resource-prefix.sh 根据uname -s/uname -m推导例如Darwin-arm64 → darwin-aarch64、Linux-x86_64 → linux-x86-64、MSYS/MINGW/CYGWIN 的 x86_64 → win32-x86-64其余组合直接报错退出。3.2 绑定生成与组装脚本打包流水线的核心是 prepare-maven-package.sh它的执行顺序大致为校验传入的原生库路径与 JNA 资源前缀必须属于上述五种之一若target/release/goose-uniffi-bindgen不存在则先执行cargo build -p goose-sdk --features uniffi --release清空旧的src/main/kotlin/io/github/aaif_goose与对应前缀的资源目录重新创建resources/prefix和resources/META-INF并把仓库根目录的LICENSE复制为META-INF/LICENSE调用 bindgengenerate --library native-lib --config crates/goose-sdk/uniffi.toml --language kotlin --no-format --out-dir kotlin_dir把生成的goose.kt等写入src/main/kotlin用一段内嵌 Python 脚本对生成的goose.kt做定点注入在findLibraryName函数体首行插入NativeLibraryLoader.ensureLoaded()从而把原生库的提前加载逻辑接入 UniFFI 生成的加载路径将src/support/kotlin下的手工支撑类NativeLibraryLoader.kt、各 provider 封装等复制进生成目录把编译好的原生库复制到resources/prefix/下。3.3 运行时加载NativeLibraryLoaderNativeLibraryLoader.kt定义了运行期的原生库装载策略internal object单例初始化即生效通过com.sun.jna.Platform判断当前 OS 与架构拼出形如darwin-aarch64/libgoose_sdk.dylib、linux-x86-64/libgoose_sdk.so、win32-x86-64/goose_sdk.dll的资源路径从classLoader中读取该资源复制到临时文件并设置deleteOnExit将临时文件绝对路径写入系统属性uniffi.component.goose.libraryOverride从而让 UniFFI 运行时改从该路径加载。由于它只在该系统属性未设置时才执行默认加载逻辑下游使用者若想指向自建的原生库可直接在 JVM 启动参数里传-Duniffi.component.goose.libraryOverride/path/to/libgoose_sdk进行覆盖——这也是本地调试 GDK 原生层的常用手段。四、本地构建从仓库根一键产出 Maven 构件README 给出的本地打包命令是just --justfile crates/goose-sdk/justfile maven-package该命令在 crates/goose-sdk/justfile 中由多个 recipe 串联而成maven-bindings profilerelease: # cargo build -p goose-sdk --features uniffi [--release] crates/goose-sdk/scripts/prepare-maven-package.sh $lib_path maven-clean: rm -rf .../maven/build .../src/main/kotlin .../src/main/resources maven-package: (maven-bindings release) cd .../maven ./gradlew --no-daemon publishToMavenLocal流程拆解如下maven-bindings以release模式编译goose-sdk启用uniffifeature得到target/release/libgoose_sdk.so/.dylib/.dll再交给prepare-maven-package.sh完成绑定生成与资源组装./gradlew --no-daemon publishToMavenLocal在maven目录下用 Gradle Wrapper 构建 jar 并发布到本地~/.m2的mavenLocal()。因此maven-package结束后io.github.aaif-goose:gdk:版本就会出现在本地 Maven 仓库中供同机其他 Gradle/Maven 工程直接消费。该工程还额外提供了just --justfile crates/goose-sdk/justfile maven-package-clean先执行maven-clean清理build、src/main/kotlin、src/main/resources等生成物再重新打包保证从干净状态重建maven-clean可单独用于清理上述生成物。构建前提需要本机具备 Rust 工具链含 Cargo、just以及可用的 JDK构建产物 target 为 JVM 11向下用新版 JDK 运行亦可。执行期间会自动触发一次 release 模式的 Rust 编译耗时较长属正常现象。五、下游验证用 Kotlin/JVM 冒烟测试打通全链路打包完成不等于可用仓库为此提供了一个真实消费端冒烟测试。相关的运行与说明见 examples/uniffi/README.md推荐直接走 justfile 一键执行just --justfile crates/goose-sdk/justfile kotlin该 recipe 等价于先跑maven-package再进入examples/uniffi/kotlin目录执行gradle --no-daemon run。手工拆开执行则是just --justfile crates/goose-sdk/justfile maven-package cd crates/goose-sdk/examples/uniffi/kotlin gradle --no-daemon run冒烟测试工程 examples/uniffi/kotlin/build.gradle.kts 有几点值得关注它以implementation(io.github.aaif-goose:gdk:${gooseSdkVersion()})声明依赖gooseSdkVersion()同样通过解析../../../Cargo.toml得到版本——这恰好验证了“下游按mavenLocal()中刚发布的版本消费本地构件”这一闭环主类为MainKtapplicationDefaultJvmArgs追加了--enable-native-accessALL-UNNAMED。README 特别说明在较新的 JDK 上需要该参数因为 GDK 借助 JNA 加载内置原生库该示例走native OpenAI provider运行前需要export OPENAI_API_KEY...。从源码结构看示例工程期望调用方 import 包名io.github.aaif_goose下的 API与 UniFFI 绑定生成的包命名空间保持一致。能在 JVM 内成功实例化 provider 并完成一次推理调用就证明本地构件的 Kotlin API 与原生库均已正确组装。六、发布到 Maven Central发布命令同样从仓库根执行just --justfile crates/goose-sdk/justfile maven-publish其核心是进入maven目录执行./gradlew --no-daemon publishAndReleaseToMavenCentral见 justfile 的maven-publishrecipe。发布依赖com.vanniktech.maven.publish插件所需的标准 Gradle 属性用于 Maven Central 凭据与内存式 PGP 签名README 给出的环境变量清单如下ORG_GRADLE_PROJECT_mavenCentralUsernameMaven CentralSonatype账号用户名ORG_GRADLE_PROJECT_mavenCentralPasswordMaven Central 账号密码或 tokenORG_GRADLE_PROJECT_signingInMemoryKeyPGP 私钥内容ASCII armored供signAllPublications()在内存中完成签名ORG_GRADLE_PROJECT_signingInMemoryKeyPassword上述私钥的密码。之所以用ORG_GRADLE_PROJECT_前缀是因为 Gradle 会把该前缀的环境变量映射为同名工程属性mavenCentralUsername、mavenCentralPassword、signingInMemoryKey、signingInMemoryKeyPasswordcom.vanniktech.maven.publish插件直接读取这些属性。实际发布时可写成export ORG_GRADLE_PROJECT_mavenCentralUsername你的用户名 export ORG_GRADLE_PROJECT_mavenCentralPassword你的密码 export ORG_GRADLE_PROJECT_signingInMemoryKey-----BEGIN PGP PRIVATE KEY BLOCK----- ... export ORG_GRADLE_PROJECT_signingInMemoryKeyPassword你的私钥密码 just --justfile crates/goose-sdk/justfile maven-publish结合 maven/build.gradle.kts 可以看到发布行为的两个关键点publishToMavenCentral(automaticRelease true)发布与自动 release 一步到位签名是条件启用的——只有工程属性里提供了signingInMemoryKey才调用signAllPublications()若跳过签名Central 会拒绝接受该构件。此外在真正对外发布前CI 需要把各平台构建出的原生库聚合进同一个 jar这一点由 CI 负责本地maven-publish只会把当前构建机前缀的原生库一并上传。七、使用者视角如何在 JVM 工程中接入 gdk对 Kotlin/JVM 使用者而言接入方式与普通 Maven 依赖无异在仓库源中声明mavenCentral()发布到 Maven Central 后即可直接解析对应 maven/settings.gradle.kts 的解析配置声明依赖io.github.aaif-goose:gdk:version例如本地验证阶段可从mavenLocal()解析发布后则改从 Central 拉取确认工程 JVM target 不低于 11GDK 以 JVM 11 编译运行在新版 JDK 上时视需要加上--enable-native-accessALL-UNNAMED这与 examples/uniffi/kotlin/build.gradle.kts 的做法一致按 UniFFI 生成的包命名空间io.github.aaif_goose导入 API运行时由 jar 内的NativeLibraryLoader依据当前 OS/架构自动挑出对应前缀下的原生库完成装载如需替换原生实现可设置-Duniffi.component.goose.libraryOverride指向自备的库文件。八、小结从 Rust 到 JVM 的一条龙发布链路回顾整条链路maven 子工程解决了一个典型的“Rust 核心、多语言外壳”分发问题版本单一事实源构件版本始终与 crates/goose-sdk/Cargo.toml 对齐杜绝手工同步组装自动化prepare-maven-package.sh 负责生成 Kotlin 绑定、注入NativeLibraryLoader.ensureLoaded()、并入支撑类、铺好 JNA 平台资源目录maven-resource-prefix.sh 负责识别本机构架一键本地闭环maven-packagekotlin冒烟测试可在单机完成“打包→消费→真实调用”的验证发布标准化maven-publish通过 vanniktech 插件向 Maven Central 发布并自动 release签名与凭据均走标准 Gradle 属性。如果你需要在非 Rust 技术栈的 JVM 项目中集成 Goose GDK或想自行扩展对更多平台/体系结构的原生库支持crates/goose-sdk/maven 目录连同 crates/goose-sdk/justfile 中maven-*系列 recipe就是最值得对照阅读的参考实现。【免费下载链接】goosean open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM项目地址: https://gitcode.com/GitHub_Trending/goose3/goose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表