golang 编译报错:matched no packages

我只是更新了一下依赖,打了一个兼容新版 v2ray 的补丁,其他啥都没动。

[    3s] ++ echo /home/abuild/rpmbuild/BUILD/go/bin
[    3s] + GOBIN=/home/abuild/rpmbuild/BUILD/go/bin
[    3s] + go install -v -p 4 -x -buildmode=pie github.com/shadowsocks/v2ray-plugin...
[    3s] go: warning: "github.com/shadowsocks/v2ray-plugin..." matched no packages
[    3s] WORK=/tmp/go-build274428667

补丁:https://github.com/shadowsocks/v2ray-plugin/pull/226.patch

保留 go.mod 应该就行了,上次更 core 也有遇到


然后还没入门 golang 的我一直没明白路径后面那三点是干嘛的……

源给我看一眼

https://build.opensuse.org/package/show/home:hillwood:branches:server:proxy/shadowsocks-v2ray-plugin

问题是你确定 server:proxy 里原来是能用的?golang-packaging 是我写的,我没打算用它打包单 binary 啊…单 binary 也用不着这个啊,你看看我的 v2ray-core 怎么打包的。不是为了兼容不支持 module 的 golang 版本的话,直接解压 go build 就出 binary 了。

“…” 是对付 golang.org/x/text 这样的包的,里面有子 importpath 比如 golang.org/x/text/transform。意思是本文件夹和子文件夹,也就是 importpath 和 sub-importpath。

v2ray-plugin 没有子文件夹,当前文件夹也不是 importpath(可导入的包即可作为 importpath 的要求是当前文件夹的所有 .go 头部必须至少有一个是 package xxx 且这个 xxx 不可以是 main),报这个 warning 是对的啊…

是的,以前都能用。你看 server:proxy 里面还有最后一次打包成功的 binary .

那三个点表示递归所有子目录(也可能是 sub packages 但效果是一样的)

哦我看到上面说了,我说的是 go 的各种命令的时候给的 ./... 这个的意思,和打包时候的还不太一样

其实在最开始的基础上保留那俩文件,解决一下补丁和 vendor 给的依赖版本不一致问题大概就好了

不过这么多变动,上游直接发个新版也就没这些问题了,上上游倒是发版发挺勤……

可能我没说明白,编译成功也许是编程意义上的未定义行为…就是你没打算让它能做那个结果它做成了,因为你没打算,所以某一次调整可能就不能做了。这个调整可能是 golang 语言层面的(因为 packaging 我看了最近几乎没有什么提交),就不好抓,因为当时编译成功的 log 现在没有。至少我是没见过谁编译 binary 来个 ./…,就像我说的,这个 warning 其实是对的…

现在我能做的就是本地重编译一下上次编译成功的 srpm,看看在现在 golang 和 golang-packaging 版本下是否还能编译成功。

自己修好了,但是原理我完全搞不明白。下载了最新的 Git 版本,保留了并更新了 go.mod go.sum vendor,其他的什么都没动就能编译了。。。。另外深度的底层也是大量的 golang,我给自己开了好多坑。。。。

https://build.opensuse.org/request/show/892070
新版 go 会强行检查 go.mod ,像以前那样删掉绕过依赖检查不行了。

我要泼冷水了…你只是侥幸成功了…

第一 binary 包 Requires 任何 golang 开头的包都没有意义

第二 你 rm -rf vendor/github.com/v2fly 操作肯定没成功,要是成功了绝对会编译失败

第三 如果 vendor 弄得对你根本不用去改 go.mod

第四 你的包似乎重新编译了整个 golang 的 source 后才开始编译这个 plugin,这个很奇怪,连 asm 都用上了,导致整个编译过程很长

第五 唯一对的就是把 … 去掉了,但是如果只是去 … 的话早就能修好,解释不了之前的编译失败

总之就是这么一次次 trival 不太好,包质量很低…

那不是 binary 包的 Requires ,而是源码包 Requires。obs 禁止网络访问,如果不想完全依赖 vendor ,那就只能用 rpm 来管理开发包的依赖。

这个有可能,我事先干掉再压缩就编译失败。我再看看编译输出。

我只是想直接利用系统内置的 v2ray 开发包,如果不改 go.mod 就会强行检查依赖给你一个错误,直接干掉又提示 v2ray-plugin 必须要有一个版本号,没有就必须重新下载。

我试过不用 golang-packaging 直接用 go build ,的确很快。不过 golang-packaging 是你写的,后来他们有没有加些东西进去我就不知道。

我不就想直接利用系统已经打包好的依赖包吗?谁叫 golang 处理依赖这么奇葩。拉源代码还得挂代理,默认代理直接撞墙,自己找的代理我都不知道有没有被投毒,vendor 里面的东西是不是和上游一样我都不敢保证。

@hillwood

第一,golang 是这样,如果是应用程序,你就不要做开发包啦,因为本地搞开发没人用你这个,都是直接 Git clone 了加几个 println 就找出错误了,真调试错误也是 gdb 直接用应用程序的 binary,不需要源代码和 debuginfo 得到的也是函数某行错这样,而且 gdb 很多时候都不好使得用 delve。它跟 C 不一样,不是不装开发包就给你一堆 “???” 看,你不 strip 的话 binary 包含了一切,golang binary 的 strip 应该是没做出来,连 buildid 都是 fake 出来的,连 debuginfo 包都一点用没有,别说你特意准备的一份 debugsource 了。如果是库,只有开发包有用,因为其实就是把 .go 源代码弄了一份。我之前是根据 buildmode 打包不同的中间二进制 .a 文件被他们否决了(其实 golang 本身就是这么打包的),所以打包的其实就是一份源代码。依赖这个库的应用程序直接把库里的源代码段 bundle 到自己的 binary 里。而且现在 vendor 很普遍,非去依赖 rpm 的开发包就是你那样来回调。

所以在我看来 v2ray-core 和 v2ray-plugin 没必须搞 BuildRequires 和开发包的 Requires,直接用 vendor 就好了。最简单的方式是自己 mkdir 造一个 GOPATH 就行了。你的意思可能是要保证 v2ray-plugin 依赖的 v2ray-core 的版本跟 v2ray-core 是一样的,没这必要 :joy: 因为永远是 v2ray-core 开发在先,plugin 在后的…

第二,你把 vendor 写成 vender 了,什么也没有做

第三,你这个问题是这样,你用系统 f2fly,删了 vendor 里的 v2fly,那它在 vendor 里面就没有了,它在 contrib/src 里,你想用就不能用 vendor,想用 vendor 就得手动把它拷贝到 vendor 里,还得保证拷贝到的版本和 go.mod 里的版本一致。也就是说现在的 golang-packaging 是在没有 modules 的时代开发的,已经落后这个时代了,他们 bash 重写了又不接受我再用 golang 本身重写。

第四,问题出在 -buildmode=pie。看起来 golang-packaging 那些翘我项目的人是想在 ppc64 和 riscv64 上面用 buildmode=pie,但是 uname -m 用错了,应该搞错了虚拟机和宿主机之类的,比如你在 ppc64 的大服务器上开虚拟机编译 x86_64,结果用了大服务器的 arch,导致了 x86_64 也开了 buildmode=pie。但是 golang 本身的源代码并没有用这个 buildmode 编译,于是连最简单的代码都要在你这边重新编译,相当于你重新编译了个 golang 再用它编译你的项目。所以专业的事情还得专业的人来。

第五,你面对的问题我第三条说了,不是 golang 的问题,而是你用的这个打包工具的问题。它不支持 modules。

我回头把我的版本再写一下,现在的 bash 版本输出太冗余不方便调试,不支持 go modules,对 … 的处理也落后了,对 importpath 的处理也有点不够智能…

再提交一遍,要还不接受那真没办法

其实我的想法每次 v2ray-core 升级 v2ray-plugin 自动跟着在新版上编译一遍,不用每次 上上游升级我都要手动去重新打包一遍 vendor 。这样操作了很久了一直都工作正常,直到这回炸了。

那你这么做也不对呀,举个例子,core 依赖 a,你编译 core 用的是 vendor,里面有 a,编译成功,同样 plugin 也编译成功。

core 更新了,依赖 a 和 b,core 的 vendor 更新了,编译成功,而 plugin 你只用 rpm 的方式给了 core 的源代码,没有 b 的源代码,plugin 的 vendor 如果重新生成,肯定有 b,而你不重新生成,plugin 自动编译还是会出错的。

而这个错误跟 plugin 用到的 core 的函数可能一点关系没有,比如 plugin 就需要 print 函数,老版 core 的这个函数只依赖 a,新版依赖 a 和 b,只是 core 的内部变化不影响 plugin,plugin 完全不需要更新代码。但你那么做 plugin 编译失败了。

所以你的想法完全是个悖论,因为 vendor 的生成是全局递归的,新版 core 引入新依赖,在 plugin 的 vendor 里也必须同样引入。不然可能出错也可能不出。要么 rpm 就把依赖拆很细,要么就 vendor,不存在一半一半的。

golang 和 Node.js 这种依赖多到令人发指的项目,分开打 rpm 只能应对依赖相对少的项目比如 docker 和你的 dde。我搞一个应用程序几十个依赖,你分开包会包死的。你搞个自动程序这么干,疯狂提交依赖包,他们就会明白了,毕竟 openSUSE 发行版不是 GitHub 的 archive 也不是 NPM registry 的 backup。换句话说也就是开发工具没做好导致发行版这边对打包 golang 或 Node.js 的项目兴趣缺乏,我也不愿意打包个 NPM 就维护 200 个包啊

1赞

permlink

我觉得如果换个角度,其实是 RPM/dkpg 由于政策或技术原因满足不了” 现代 “的开发方式。

golang/nodejs/rust 那样一个功能就 import 一个新的依赖,其实蛮符合 *nix 讲究的 "Do One Thing and Do It Well”。


C++/C 经常需要一个包罗万象的依赖 Glib, Qt, Boost, POCO, icu 或者像 C# 直接需要微软写的巨大无比的 .NET 框架。其中的一个原因就是他们管理依赖很麻烦。

就像许多 C++/C 写的程序,目录里面经常有个 Util 放一些常见的东西,比如说 fcitx-utilssystemd/basicgit/string-list

如果 C++/C 管理依赖方便一些,那些东西就会变成很小的工具库,然后被许多项目依赖,共同维护。对于 God 级别还有靠写开源软件吃饭的人,写那些东西当然不难,但是如果从社区整体来看,那些重复的劳动似乎没有什么必要。

因为 C++/C 自带的处理字符串的功能不怎么好用,所以许多项目都有一个很相似的 String_util.c 不断地重复实现着几乎一模一样的功能 =。=

而对比起来,那些新的语言想要更多操作字符串的函数 -> import string.go,要另一种风格的函数式编程 -> import lodash.js,要解压 zip -> import zip ,要拯救世界 import world-saver


从库的维护的角度来说,以开发 Glib 的人为例,他们需要维护处理字符串,时间,线程,数据结构,regex,配置文件等等一大堆东西。

对比起来,新语言单独模块的维护者只需要维护可能连一张纸不到的函数列表。我只负责一个很专门的领域,由于维护成本不高,我可以把 API 设计的特别精美,而且可以及时地确保能在新版本的操作系统/编译器上使用。

另外对于开发者来说不想要 Glib 里面处理线程的部分,为什么要添加一个给巨大无比的依赖。

依赖一个 Glib 然后让 GNOME 的开发者决定你能用什么 VS 亲自挑选各个模块的组件来组装一个对应专门程序的 Util-lib,按需添加, 且每一个模块都是小而精致


另外从开发方式来看, 那些新语言也更符合开源的精神。

我用一个库,但是原作者开发的太慢,或者不喜欢原作者的方向,或者需要添加一些只有我一个人需要的增强功能。只要 fork 一下,然后 import myrepo/string.go

如果我制作了这个库的变种,可以帮助另一堆用户群,凭什么只有原作者的那个在发行版的仓库里面。


所以我认为是 RPM/Dkpg 落后于那些现代语言自带的包管理器,让他们去适应 rpm/dkpg 应该是在开倒车。

能跨平台现在都是现代语言的基本功能了,这一条就让他们有足够的理由写自己的包管理器。

操作系统的包管理在面对这些语言的时候,应该直接放弃处理依赖,直接交给编程语言自己解决。

并且标准化每个语言单独使用的目录(类似于 MacOS 的那个 /library & ~/Library,标准化一个 /usr/library/python, /usr/library/node, /usr/library/latex

而且从云端 Maven/Crates/NPM/Pypi/Melpa/OPAM/Hackage/Quicklisp/CPAN/CTAN 直接拉取依赖本来就解决了一部分依赖处理麻烦的问题,

不如干脆让操作系统的包管理,直接通过 pip/cargo/opam 之类的东西直接和各个语言的仓库连通,并且自动地在本地注册 python::xxxx, go::yyyy, rust::zzz 的包。编程语言的包管理同时还会注册一下依赖的包。

如果一个软件依赖 Rust::string_util,用操作系统的包管理器安装的时候,直接从 Crates.io 查询并安装,直接省略 -> 打包-> 弄进操作系统的仓库的流程。

至少 python/go/rust/ocaml/java… 的包管理器本来就不依赖具体的发行版,这样顺便还可以进一步减少不同发行版之间没有必要的区别。

这个脑洞应该不会在 RPM/dkpg 上实现,或许可以 Hack 一下操作系统的包管理器 (arch/void/),从编程语言的仓库安装以后,通知他们这个包有了!这样操作系统的包管理器就变成了 Meta-package manager(大雾)。