0%

记录一次烦人的编译出错问题

记录一次烦人的编译问题,因为这个过程可以能可能会对以后解决类似问题有一定的参考性,特地做个记录。

我想做什么

我之所以要做这个,是因为我想借用murmel库,在murmel基础上拓展功能。其中murmel的消息类型定义在rust-bitcoin库中。为了拓展功能,我准备先fork rust-bitcoin魔改rust-bitcoin,加我想要的消息。然后fork murmel,魔改murmel外带让murmel依赖我修改过的rust-bitcoin。

听起来有点像把大象放冰箱需要几步。是的,我先改rust-bitcoin(这个库是序列化比特币消息的),然后我修改murmel,让他依赖改过的rust-bitcoin。最后我得到一个可以用的SPV节点。完工!

我遇到了什么问题

先魔改rust-bitcoin。这个很顺利。然后fork了murmel,然后编译不过。此处应该有编译错误(但我不想列出来),总体意思是说有的类型 Hash 没有实现serde的部分trait。

我的折腾过程

这个问题很奇怪,我之前git clone的版本是可以编译的,而且看提交记录这个项目这几个月并没有更新。然后编译之前备份的版本,可以编译。clone的新版本编译,又失败。那先对比下Cargo.lock文件。使用Beyond Compare对比。发现两份文件的真的不一样。问题来了,代码一样,一个可以编译一个不可以。所以先替换了lock,发现了一个事实:用之前可以编译版本的lock文件就可以正常编译,如果删出lock文件之后自动生成则无法编译。

然后开始回退版本,因为之前可以编译。猜想是版本问题,使用git log 查看版本。神奇的情况出现了,一个一个rev回退之后竟然都不可以编译。一般来说回退版本之后基本是可以找到可以编译版本的。然后查看Tag,发现只有两个Tag,直接切换到对应的Tag上竟然也不可以编译。算了,那直接去下载github上的release版本。发现还是不可以编译…

经过之前的折腾,完全不知所措。本来想的是一个版本一个版本的回退,找到一个可以编译的版本,然后查看他依赖的哪个版本的rust-bitcoin,然后把这个rust-bitcoin改成本地依赖。现在发现都不可以编译…没办法需要阅读《Cargo Book》看看有没有解决办法。毕竟他之前是可以编译的。看了一夜《Cargo Book》之后发现事实:

  1. Cargo.toml文件中version的依赖方式是依托于cartes.io的,并不是依赖github。
  2. 上传到crates.io的包一定是可以编译的。

基于以上两点,那可以考虑从crates.io上clone一个包下来进行编译。需要安装一个第三方包cargo clone。使用cargo clone拉一个murmel。发现可以编译。OK,现在问题解决一大半了。crates.io上的版本是一定可以编译的。然后再使用另一个第三方的包cargo tree去分析下可以编译版本的murmel的依赖关系。注意:这个cargo tree是依赖lock文件分析依赖关系的,必须在一个真正的项目下下去运行,加入在work-space的目录下是无法分析的。可以看到出对应的rust-bitcoin版本。接下来直接使用cargo clone去拉可以编译版本的rust-bitcoin。下载下来之后尝试下是否可以直接编译。发现rust-bitcoin直接编译没有问题。然后把murmel中的依赖指向我们可以编译的rust-bitcoin。注意:murmel和rust-bitcoin必须在同一个大的cargo项目下。否则编译murmel的时候找不到rust-bitcoin。可以使用work-space的方式去组织项目。遗憾的是这么做竟然还不行。

emmm,到这里快放弃了。不过别慌,再看看《Cargo Book》。还好,cargo book 上还有一个解决办法那就是使用Cargo.Patch。

https://doc.rust-lang.org/edition-guide/rust-2018/cargo-and-crates-io/replacing-dependencies-with-patch.html

https://learnku.com/docs/cargo-book/2018/specifying-dependencies/4773

两份资料一起看。我们直接使用patch。然后又出现问题了……原来我们的项目是一个workspace下面有两个平行的项目murmel 和 rust-bitcoin。patch不能这么做。ok那调整文件结构。把rust-bitcoin调整到murmel文件夹中,和murmel的Cargo.toml一级。然后在Cargo.toml在使用如下

1
2
3
4
5
6
[dependencies.bitcoin]
version = "0.21"
features = ["serde"]

[patch.crates-io]
bitcoin = { path = "bitcoin" }

patch这一行是新添加的。path代表路径,从crates.io上下下来的rust-bitcoin文件夹名称为“bitcoin”。

编译通过。

然后murmel和rust-bitcoin都在本地了。想改哪个该哪个。完工。

最后,还有改进余地。把修改过rust-bitcoin上传到github,然后在patch中依赖{git=”xxxxx.git”,rev = “xxxxx”}。更适合代码管理,不用依赖本地了。我因为要频繁修改代码,所以先不依赖github上的版本。

总结知识点

Cargo中依赖方式有三种

  1. 依赖本地
  2. 依赖git {git = “xxx”, rev = “xxxx”}
  3. 依赖crates.io上的包。

最终解决的办法是

  1. 安装Cargo tree 和 Cargo Clone
  2. 从crates上clone一个可以编译的murmel
  3. 使用cargo tree看依赖关系。(其实直接看Cargo.toml也可以看出来,问题在于看起来都没差别但是就是编译不过,以前的备份又确实可以编译过)
  4. 使用cargo clone提取能编译版本的rust-bitcoin
  5. 把murmel依赖的能编译的版本的rust-bitcoin放倒murmel目录下
  6. 使用cargo patch打补丁,使murmel依赖的rust-bitcoin指向本地。
  7. 编译通过了,现在murmel和rust-bitcoin都在本地,可以自由更改了。
  8. 可以把自己改过的rust-bitcoin上传到github。Patch指向github从版本管理角度更好点。

穷举法解决问题。要是还不行,那我真没办法了。