複数のcrate-typeを持つプロジェクトのビルド
実行可能ファイルを作りたい
ライブラリーを提供したい
ライブラリーとして
多言語(Ruby)とかからFFI(Fiddle)で使えるようにしたい という時、FFI用の関数は、本体とは別に作るのを見る。
が、まとめてできない物だろうかと思って試してみる。
#[no_mangle]がポイントの気がしてる
環境
code:shell
% cat /etc/os-release; rustc -V; cargo -V
NAME="Ubuntu"
VERSION="18.04.4 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.4 LTS"
VERSION_ID="18.04"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
rustc 1.41.0 (5e1a79984 2020-01-27)
cargo 1.41.0 (626f0f40e 2019-12-03)
ソースコード
code:shell
% tree
.
├── Cargo.lock
├── Cargo.toml
└── src
├── lib.rs
└── main.rs
1 directory, 4 files
プロジェクト定義
cargo new epub-cacheで作ったやつ(つまりデフォルトの--binが暗黙に指定されてる)
code:Cargo.toml
name = "epub-cache"
version = "0.1.0"
edition = "2018"
これでビルドすると、実行可能ファイル(epub-cache)とRust用ライブラリーファイル(libepub_cache.rlib)ができる
code:shell
% cargo build
Compiling epub-cache v0.1.0 (/home/kitaitimakoto/src/gitlab.com/KitaitiMakoto/epub-cache)
% ls target/debug/epub-cache
target/debug/epub-cache
% ls target/debug/libepub_cache.rlib
target/debug/libepub_cache.rlib
因みにsrc/lib.rsを作らなければ、libepub_cache.rlibは作られない。
で、crate-typeにcdylibを追加してみる。
code:Cargo.toml
name = "epub-cache"
version = "0.1.0"
edition = "2018"
code:shell
% ls target/debug/libepub_cache.so
target/debug/libepub_cache.so
FFIで使いたいのでシンボルを見てみる。
code:shell
% nm target/debug/libepub_cache.so
0000000000201d80 d _DYNAMIC
0000000000201f80 d _GLOBAL_OFFSET_TABLE_
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U _Unwind_GetDataRelBase@@GCC_3.0
U _Unwind_GetIPInfo@@GCC_4.2.0
U _Unwind_GetLanguageSpecificData@@GCC_3.0
U _Unwind_GetRegionStart@@GCC_3.0
U _Unwind_GetTextRelBase@@GCC_3.0
U _Unwind_SetGR@@GCC_3.0
U _Unwind_SetIP@@GCC_3.0
0000000000000b10 t _ZN12panic_unwind3imp14find_eh_action28_$u7b$$u7b$closure$u7d$$u7d$17h2fbb20131eb1d53dE
0000000000000b20 t _ZN12panic_unwind3imp14find_eh_action28_$u7b$$u7b$closure$u7d$$u7d$17hb04ec8ed8afdd550E
0000000000000990 t _ZN12panic_unwind5dwarf2eh20read_encoded_pointer17h1a788cfdd1899617E
0000000000000920 t _ZN3std3sys4unix4args3imp15ARGV_INIT_ARRAY12init_wrapper17h4c7208ee46ae085aE
0000000000201d08 t _ZN3std3sys4unix4args3imp15ARGV_INIT_ARRAY17h76866969642629ceE
0000000000202010 b _ZN3std3sys4unix4args3imp4ARGC17hb4a2549ef638feafE
0000000000202018 b _ZN3std3sys4unix4args3imp4ARGV17hb4cd8868800b3590E
0000000000202020 b _ZN3std3sys4unix4args3imp4LOCK17h3a2d6165e0df96d1E
0000000000000960 t _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h720c016ae672bc96E
0000000000000970 t _ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17hac34a1796a6eb4ceE
0000000000000980 t _ZN4core3ptr18real_drop_in_place17h3c71856ced99f170E
000000000000108c r __FRAME_END__
0000000000000ebc r __GNU_EH_FRAME_HDR
0000000000202008 d __TMC_END__
0000000000202008 b __bss_start
w __cxa_finalize@@GLIBC_2.2.5
00000000000008d0 t __do_global_dtors_aux
0000000000201d18 t __do_global_dtors_aux_fini_array_entry
0000000000202000 d __dso_handle
0000000000201d10 t __frame_dummy_init_array_entry
w __gmon_start__
0000000000202008 d _edata
0000000000202048 b _end
0000000000000e58 t _fini
0000000000000800 t _init
0000000000202008 b completed.7697
0000000000000840 t deregister_tm_clones
0000000000000910 t frame_dummy
U pthread_mutex_lock@@GLIBC_2.2.5
U pthread_mutex_unlock@@GLIBC_2.2.5
0000000000000880 t register_tm_clones
0000000000000b30 T rust_eh_personality
ううーむ、だめぽい? やっぱり#[no_mangle]が必要なのか?
でも、これでだめならcdylibって何に使うんだろう・・・。
#[no_mangle]してみる。
code:src/main.rs
use epub_cache;
fn main() {
let epub_path = std::env::args().nth(1).expect("No EPUB file specified");
let cache_path = epub_cache::cache_path_from_path(&epub_path);
println!("{}", cache_path.to_string_lossy());
}
pub extern fn cache_path_from_path<P: AsRef<Path>>(path: P) {
println!("{}", std::path::Path::new(".cache").join(path).to_string_lossy());
}
code:shell
% cargo clean; cargo build
Compiling epub-cache v0.1.0 (/home/kitaitimakoto/src/gitlab.com/KitaitiMakoto/epub-cache)
warning: functions generic over types or consts must be mangled
--> src/main.rs:10:1
|
| ------------ help: remove this attribute
10 | / pub extern fn cache_path_from_path<P: AsRef<std::path::Path>>(path: P) {
11 | | println!("{}", std::path::Path::new(".cache").join(path).to_string_lossy());
12 | | }
| |_^
|
= note: #[warn(no_mangle_generic_items)] on by default
% nm target/debug/libepub_cache.so | rg cache_from
シンボルがない。
単に、引数がCじゃなくてRustの型だからだめ、とかあるかな。
mainじゃなくてlibに書くべきだった。
code:src/lib.rs
use std::path::{Path,PathBuf};
pub fn cache_path_from_path<P: AsRef<Path>>(path: P) -> PathBuf {
Path::new(".cache").join(path)
}
pub extern fn cache_path() {
println!("Hello, cache_path_from_path");
}
code:shell
% cargo clean; cargo build
Compiling epub-cache v0.1.0 (/home/kitaitimakoto/src/gitlab.com/KitaitiMakoto/epub-cache)
% nm target/debug/libepub_cache.so | rg cache
0000000000003780 T cache_path
おっけー。
中で、Rustの関数読んでみる。
code:src/lib.rs
use std::path::{Path,PathBuf};
pub fn cache_path_from_path<P: AsRef<Path>>(path: P) -> PathBuf {
Path::new(".cache").join(path)
}
pub extern fn cache_path() {
let path_buf = cache_path_from_path("path/to/doc.epub");
println!("{}", path_buf.to_string_lossy());
}
code:shell
% cargo clean; cargo build
Compiling epub-cache v0.1.0 (/home/kitaitimakoto/src/gitlab.com/KitaitiMakoto/epub-cache)
cargo build 1.33s user 2.41s system 112% cpu 3.305 total
% nm target/debug/libepub_cache.so | rg cache
0000000000003fd0 t _ZN10epub_cache20cache_path_from_path17h71e42a73d8edd108E
0000000000004580 T cache_path
RubyからFFIできるか確かめてみる。
code:epub-cache.rb
require "fiddle/import"
module EpubCache
extend Fiddle::Importer
dlload "target/debug/libepub_cache.so"
extern "void cache_path()"
end
EpubCache.cache_path
code:shell
% ruby ./epub-cache.rb
.cache/path/to/doc.epub
おっけー。
次は、引数の型がRustの型のやつ。cache_path_from_pathに#[no_mangle]してみる。
code:shell
% cargo clean; cargo build
Compiling epub-cache v0.1.0 (/home/kitaitimakoto/src/gitlab.com/KitaitiMakoto/epub-cache)
warning: functions generic over types or consts must be mangled
--> src/lib.rs:4:1
|
| ------------ help: remove this attribute
4 | / pub fn cache_path_from_path<P: AsRef<Path>>(path: P) -> PathBuf {
5 | | Path::new(".cache").join(path)
6 | | }
| |_^
|
= note: #[warn(no_mangle_generic_items)] on by default
% nm target/debug/libepub_cache.so | rg cache
0000000000004580 T cache_path
0000000000003fd0 t cache_path_from_path
さてでは、#[no_mangle]を外したい。無理っぽいが。
code:src/lib.rs
use std::path::{Path,PathBuf};
pub fn cache_path_from_path<P: AsRef<Path>>(path: P) -> PathBuf {
Path::new(".cache").join(path)
}
pub extern fn cache_path() {
let path_buf = cache_path_from_path("path/to/doc.epub");
println!("{}", path_buf.to_string_lossy());
}
code:shell
% cargo clean; cargo build
Compiling epub-cache v0.1.0 (/home/kitaitimakoto/src/gitlab.com/KitaitiMakoto/epub-cache)
% ruby ./epub-cache.rb
Traceback (most recent call last):
3: from ./epub-cache.rb:3:in `<main>'
2: from ./epub-cache.rb:6:in `<module:EpubCache>'
1: from /usr/local/lib/ruby/2.7.0/fiddle/import.rb:172:in `extern'
/usr/local/lib/ruby/2.7.0/fiddle/import.rb:299:in `import_function': cannot find the function: cache_path() (Fiddle::DLError)
ですよねー。
これだと、FFI専用の[no_mangle]した関数をそのためだけに定義しないといけないことになるなあ。。。しょうがないんだろうか。*.so作る時だけマングリングしないで、*.rlib作る時はマングリングする、とできたらいいんだけど。