changelog shortlog graph tags branches changeset files file revisions raw help

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

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