changelog shortlog graph tags branches changeset files file revisions raw help

Mercurial > org > blog / annotate draft/dylib-skel.org

changeset 28: 6d54ccb29de4
parent: 1204cefcfd28
author: Richard Westhaver <ellis@rwest.io>
date: Sun, 18 Aug 2024 22:16:12 -0400
permissions: -rw-r--r--
description: weekend warrior
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
1
 #+title: Shared Library Skeletons
21
1204cefcfd28 style updates
Richard Westhaver <ellis@rwest.io>
parents: 20
diff changeset
2
 #+setupfile: ../../clean.theme
1204cefcfd28 style updates
Richard Westhaver <ellis@rwest.io>
parents: 20
diff changeset
3
 #+header-args: :eval never
28
6d54ccb29de4 weekend warrior
Richard Westhaver <ellis@rwest.io>
parents: 21
diff changeset
4
 #+property: header-args :eval no-export
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
5
 * Overview
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
6
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
7
 :ID:       748ba6e4-60db-4ff7-9d78-12c3f67644d8
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
8
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
9
 + CODE :: [[https://lab.rwest.io/packy/stash/dysk][packy/stash/dysk]]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
10
 Our core languages are [[https://www.rust-lang.org/][Rust]] and [[https://lisp-lang.org/][Lisp]] - this is the killer combo which will allow NAS-T
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
11
 to rapidly develop high-quality software. As such, it's crucial that these two very
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
12
 different languages (i.e. compilers) are able to interoperate seamlessly.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
13
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
14
 Some interop methods are easy to accomodate via the OS - such as IPC or data sharing,
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
15
 but others are a bit more difficult.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
16
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
17
 In this 2-part series we'll build a FFI bridge between Rust and Lisp, which is something
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
18
 that /can/ be difficult, due to some complications with Rust and because this is not the
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
19
 most popular software stack (yet ;). This is an experiment and may not make it to our
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
20
 code-base, but it's definitely something worth adding to the toolbox in case we need it.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
21
 ** FFI
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
22
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
23
 :ID:       0e490b3b-0c15-4963-9d43-7d2a689ec371
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
24
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
25
 The level of interop we're after in this case is [[https://en.wikipedia.org/wiki/Foreign_function_interface][FFI]].
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
26
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
27
 Basically, calling Rust code from Lisp and vice-versa. There's an article about calling
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
28
 Rust from Common Lisp [[https://dev.to/veer66/calling-rust-from-common-lisp-45c5][here]] which shows the basics and serves as a great starting point
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
29
 for those interested.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
30
 *** Rust ABI
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
31
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
32
 :ID:       6c24dc95-eea6-44fb-8c7f-bb49227ca7bf
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
33
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
34
 The complication(s) with Rust I mentioned early is really just that /it is not C/. =C=
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
35
 is old, i.e. well-supported with a stable ABI, making the process of creating bindings
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
36
 for a C library a breeze in many languages.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
37
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
38
 For a Rust library we need to first appease the compiler, as explained in [[https://doc.rust-lang.org/nomicon/ffi.html#calling-rust-code-from-c][this section]]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
39
 of the Rustonomicon. Among other things it involves changing the calling-convention of
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
40
 functions with a type signature and editing the Cargo.toml file to produce a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
41
 C-compatible ABI binary. The Rust default ABI is unstable and can't reliably be used
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
42
 like the C ABI can.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
43
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
44
 [[https://github.com/rodrimati1992/abi_stable_crates][abi_stable_crates]] is a project which addresses some of the ABI concerns, presenting a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
45
 sort of ABI-API as a Rust library. Perhaps this is the direction the ecosystem will go
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
46
 with in order to maintain an unstable ABI, but for now there is no 'clear' pathway for a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
47
 friction-less FFI development experience in Rust.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
48
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
49
 *** Overhead
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
50
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
51
 :ID:       faf8fbcb-3403-4e2f-b881-ef1404ad9780
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
52
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
53
 Using FFI involves some overhead. Check [[https://github.com/dyu/ffi-overhead][here]] for an example benchmark across a few
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
54
 languages. While building the NAS-T core, I'm very much aware of this, and will need a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
55
 few sanity benchmarks to make sure the cost doesn't outweigh the benefit. In particular,
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
56
 I'm concerned about crossing multiple language barriers (Rust<->C<->Lisp).
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
57
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
58
 * basic example
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
59
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
60
 :ID:       ab04fff7-51c9-4f1c-b61b-fa4739932343
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
61
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
62
 ** Setup
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
63
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
64
 :ID:       08420b18-3856-4b62-9523-98fb3398afca
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
65
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
66
 For starters, I'm going to assume we all have Rust (via [[https://rustup.rs/][rustup]]) and Lisp ([[https://www.sbcl.org/][sbcl]] only)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
67
 installed on our GNU/Linux system (some tweaks needed for Darwin/Windows, not covered in
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
68
 this post).
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
69
 *** Cargo
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
70
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
71
 :ID:       cfeb1299-67c5-4a64-863e-f214e195e176
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
72
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
73
 Create a new library crate. For this example we're focusing on a 'skeleton' for
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
74
 /dynamic/ libraries only, so our experiment will be called =dylib-skel= or *dysk* for
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
75
 short.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
76
 src_sh[:exports code]{cargo init dysk --lib && cd dysk} 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
77
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
78
 A =src/lib.rs= will be generated for you. Go ahead and delete that. We're going to be
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
79
 making our own =lib.rs= file directly in the root directory (just to be cool).
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
80
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
81
 The next step is to edit your =Cargo.toml= file. Add these lines after the =[package]=
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
82
 section and before =[dependencies]=:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
83
 #+begin_src conf-toml
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
84
 [lib]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
85
 crate-type = ["cdylib","rlib"]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
86
 path = "lib.rs"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
87
 [[bin]]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
88
 name="dysk-test"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
89
 path="test.rs"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
90
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
91
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
92
 This tells Rust to generate a shared C-compatible object with a =.so= extension which we
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
93
 can open using [[https://man.archlinux.org/man/dlopen.3.en][dlopen]].
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
94
 *** cbindgen
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
95
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
96
 :ID:       435d84d6-c959-4bf6-ad37-fb6e524b54ff
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
97
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
98
 **** install
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
99
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
100
 :ID:       15115ab8-e7b4-4050-9567-bf026cc140d1
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
101
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
102
 Next, we want the =cbindgen= program which we'll use to generate header files for
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
103
 C/C++. This step isn't necessary at all, we just want it for further experimentation.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
104
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
105
 src_sh[:exports code]{cargo install --force cbindgen}
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
106
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
107
 We append the =cbindgen= crate as a /build dependency/ to our =Cargo.toml= like so:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
108
 #+begin_src conf-toml
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
109
 [build-dependencies]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
110
 cbindgen = "0.24"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
111
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
112
 **** cbindgen.toml
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
113
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
114
 :ID:       21d776b7-9f8d-445c-83c2-8e0c28751827
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
115
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
116
 #+begin_src conf-toml :tangle cbindgen.toml
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
117
 language = "C"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
118
 autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
119
 include_version = true
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
120
 namespace = "dysk"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
121
 cpp_compat = true
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
122
 after_includes = "#define DYSK_VERSION \"0.1.0\""
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
123
 line_length = 88
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
124
 tab_width = 2
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
125
 documentation = true
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
126
 documentation_style = "c99"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
127
 usize_is_size_t = true
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
128
 [cython]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
129
 header = "dysk.h"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
130
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
131
 **** build.rs
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
132
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
133
 :ID:       469dd142-bafa-4076-8ba9-baf088e60260
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
134
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
135
 #+begin_src rust :tangle build.rs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
136
 fn main() -> Result<(), cbindgen::Error> {
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
137
   if let Ok(b) = cbindgen::generate(std::env::var("CARGO_MANIFEST_DIR").unwrap()) {
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
138
     b.write_to_file("dysk.h"); Ok(())}
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
139
   else { panic!("failed to generate dysk.h from cbindgen.toml") } }
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
140
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
141
 ** lib.rs
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
142
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
143
 :ID:       e9d50e77-4d1e-4d38-90aa-9977d8c6c888
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
144
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
145
 #+begin_src rust :tangle lib.rs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
146
 //! lib.rs --- dysk library
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
147
 use std::ffi::{c_char, c_int, CString};
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
148
 #[no_mangle]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
149
 pub extern "C" fn hello() -> *const c_char {
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
150
   CString::new("hello from rust").unwrap().into_raw()}
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
151
 #[no_mangle]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
152
 pub extern "C" fn plus(a:c_int,b:c_int) -> c_int {a+b}
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
153
 #[no_mangle]
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
154
 pub extern "C" fn plus1(n:c_int) -> c_int {n+1}
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
155
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
156
 ** test.rs
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
157
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
158
 :ID:       84b5dffe-7171-4d72-9788-f288e3185070
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
159
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
160
 #+begin_src rust :tangle test.rs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
161
 //! test.rs --- dysk test
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
162
 fn main() { let mut i = 0u32; while i < 500000000 {i+=1; dysk::plus1(2 as core::ffi::c_int);}}
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
163
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
164
 ** compile
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
165
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
166
 :ID:       30f4fb3e-0757-4df6-947f-8d1e11edabf9
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
167
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
168
 #+begin_src sh
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
169
 cargo build --release
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
170
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
171
 ** load from SBCL
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
172
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
173
 :ID:       69b517f2-648d-4e44-b956-7879b3dadf99
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
174
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
175
 #+begin_src lisp :tangle dysk.lisp
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
176
 ;;; dysk.lisp
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
177
 ;; (dysk:hello) ;; => "hello from rust"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
178
 (defpackage :dysk
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
179
   (:use :cl :sb-alien)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
180
   (:export :hello :plus :plus1))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
181
 (in-package :dysk)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
182
 (load-shared-object #P"target/release/libdysk.so")
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
183
 (define-alien-routine hello c-string)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
184
 (define-alien-routine plus int (a int) (b int))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
185
 (define-alien-routine plus1 int (n int))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
186
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
187
 ** benchmark
19
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
188
 :PROPERTIES:
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
189
 :ID:       86e4195d-601b-4736-b852-1209a6d38bdf
Richard Westhaver <ellis@rwest.io>
parents: 18
diff changeset
190
 :END:
18
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
191
 #+begin_src shell
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
192
 time target/release/dysk-test
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
193
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
194
 #+begin_src lisp :tangle test.lisp
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
195
 (time (dotimes (_ 500000000) (dysk:plus1 2)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
196
 #+end_src
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
197