問題描述 通常我們在rust項目中引入第三方依賴包時,會直接指定包的版本,這種方式指定後,Cargo在編譯時會從crates.io這個源中下載這些依賴包。 [package] name = "foo" version = "0.1.0" edition = "2021" [dependencies] ...
問題描述
通常我們在rust項目中引入第三方依賴包時,會直接指定包的版本,這種方式指定後,Cargo在編譯時會從crates.io這個源中下載這些依賴包。
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[dependencies]
j4rs = 0.15.3
比如這裡我們就在項目中引用了j4rs
這個包,這個包的主要作用是可以實現從Rust代碼中調用Java代碼。
博主在使用這個包時發現,crates.io上發佈的最新版本0.15.3
有bug,這個版本依賴了logback的新版本,而logback的新版本使用了Java 11進行編譯。這就導致了j4rs 0.15.3
這個版本不支持Java 8。
於是博主在github上向作者提了issue,作者很快就做了修改,並更新到了github項目的master分支上。然而作者卻沒有向crates.io推送最新版本的包,我們想用最新版本就不能直接飲用crates.io上的版本。
想要解決這個問題也很簡單,我們可以直接引用源碼作為依賴,主要有一下兩種方式。
方式一
Cargo支持直接引用git最新版本的代碼
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[dependencies]
j4rs = { git = "https://github.com/astonbitecode/j4rs" }
方式二
引用本地源碼
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[dependencies]
j4rs = { path = "../j4rs/rust" }
當我們引用三方包的源碼後,編譯時Cargo也會根據三方包的Cargo配置編譯這些三方包的源碼,然後把編譯的結果輸出到本項目的target/[debug/release]/deps目錄下,這樣本項目就可以使用這些三方包了。
博主在引用j4rs
這個三方包時遇到了這個問題:release編譯時,編譯器提示,j4rs編譯的輸出命名衝突。
解決方法
查看j4rs
源碼中的Cargo.toml
文件
[package]
name = "j4rs"
version = "0.15.4"
...
[badges]
travis-ci = { repository = "astonbitecode/j4rs", branch = "master" }
[lib]
name = "j4rs"
crate-type = ["rlib", "cdylib"]
path = "src/lib.rs"
可以發現crate-type
這個裡配置了兩種編譯結果crate類型。
crate類型
bin: 二進位可執行 crate,編譯出的文件為二進位可執行文件。必須要有 main 函數作為入口。這種 crate 不需要在 Cargo.toml 中或 --crate-type 命令行參數中指定,會自動識別。
lib: 庫crate。它其實並不是一種具體的庫,它指代後面各種庫 crate 中的一種,可以認為是一個代理名稱(alias)。通常來講,如果什麼都不配置,預設指的是 rlib, 會生成 .rlib 的文件。
dylib: 會在編譯的時候,生成動態庫(Linux 上為 .so, MacOS 上為 .dylib, Windows 上為 .dll)。動態庫是平臺相關的庫。動態庫在被依賴並鏈接時,不會被鏈接到目標文件中。這種動態庫只能被 Rust 寫的程式(或遵循 Rust 內部不穩定的規範的程式)調用。這個動態庫可能依賴於其它動態庫(比如,Linux 下用 C 語言寫的 PostgreSQL 的 libpq.so,或者另一個編譯成 "dylib" 的 Rust 動態庫)。
staticlib: 靜態庫。編譯會生成 .a 文件(在 Linux 和 MacOS 上),或 .lib 文件(在 Windows 上)。編譯器會把所有實現的 Rust 庫代碼以及依賴的庫代碼全部編譯到一個靜態庫文件中,也就是對外界不產生任何依賴了。這特別適合將 Rust 實現的功能封裝好給第三方應用使用。
cdylib: C規範動態庫。與 dylib 類似,也會生成 .so, .dylib 或 .dll 文件。但是這種動態庫可以被其它語言調用(因為幾乎所有語言都有遵循 C 規範的 FFI 實現),也就是跨語言 FFI 使用。這個動態庫可能依賴於其它動態庫(比如,Linux 下用 C 語言寫的 PostgreSQL 的 libpq.so)。
rlib: rlib 是 Rust Library 特定靜態中間庫格式。如果只是純 Rust 代碼項目之間的依賴和調用,那麼,用 rlib 就能完全滿足使用需求。rlib 實現為一個 ar 歸檔文件。rlib 中包含很多 metadata 信息(比如可能的上游依賴信息),用來做後面的 linkage。可以指定生成 rlib,但是一般沒必要設置,因為預設 lib 就是 rlib。rlib 是平臺(Linux, MacOS, Windows ...)無關的。
proc-marco: 這種 crate 裡面只能導出過程巨集,被導出的過程巨集可以被其它 crate 引用。
具體解釋
根據文檔j4rs
設置的兩種crate類型,rlib
表示rust本身定義的中間結果,如果rust代碼互相引用,直接使用這種類型就可以。cdylib
是符合c標準的動態鏈接庫,這種方式編譯的結果可以被其他語言作為動態庫使用。
當我們進行release編譯時,Cargo會根據配置幫我們編譯j4rs
這兩種格式的目標輸出。
這時Cargo就會提示我們輸出了一個libj4rs.rlib
文件,又要輸出一個libj4rs.so
文件,這兩個庫文件名字一樣,衝突了。這會導致我們的代碼在鏈接j4rs
時無法選擇應該使用哪個庫。
因此解決方法是:只要在j4rs
的源碼里將Cargo.toml
文件中的配置crate-type = ["rlib", "cdylib"]
改為crate-type = ["rlib"]
就可以了。