Skip to content

Commit 7626f5a

Browse files
committed
feat: allow loading of Pluto standard libraries
1 parent 798ebf2 commit 7626f5a

File tree

11 files changed

+222
-33
lines changed

11 files changed

+222
-33
lines changed

Cargo.lock

Lines changed: 22 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pluto-build"
3-
version = "0.1.0-0.10.4"
3+
version = "0.2.0-0.10.4"
44
edition = "2021"
55
authors = ["Sculas <[email protected]>"]
66
repository = "https://github.com/Sculas/pluto-build-rs"
@@ -10,7 +10,7 @@ license = "MIT"
1010
description = "A Rust library for compiling and linking Pluto into your Rust project."
1111

1212
[workspace]
13-
members = ["testcrate"]
13+
members = ["pluto-ffi", "testcrate"]
1414

1515
[dependencies]
1616
cc = { version = "1.2", features = ["parallel"] }

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ A Rust library for compiling and linking [Pluto](https://pluto-lang.org/) into y
99
Add the following to your `Cargo.toml`:
1010

1111
```toml
12+
[dependencies]
13+
# Add the `pluto-ffi` crate to your project if you want to use any of Pluto's standard libraries.
14+
pluto-ffi = "0.1.0"
15+
1216
[build-dependencies]
1317
# pluto-build's version is suffixed with the Pluto version it was built against.
1418
# It's recommended to pin this to the exact version of Pluto you want to use.
15-
pluto-build = "=0.1.0-0.10.4"
19+
pluto-build = "=0.2.0-0.10.4"
1620
```
1721

1822
Then, in your `build.rs` file, add the following:
@@ -38,9 +42,19 @@ This will compile Pluto and link it statically into your project. You can then u
3842
mlua = { version = "0.10", features = ["lua54", "external"] }
3943
```
4044

41-
## Known Issues
45+
To be able to use Pluto's standard libraries, you must add the following after you've created your `Lua` instance:
46+
47+
```rust
48+
let lua = mlua::Lua::new();
49+
pluto_ffi::load_libraries!(&lua)?;
50+
```
51+
52+
Or, if you want to load only specific libraries:
4253

43-
- The `mlua` crate doesn't call `luaL_openlibs` at the moment, which means none of Pluto's standard libraries are available. I'm working on a fix for this issue.
54+
```rust
55+
let lua = mlua::Lua::new();
56+
pluto_ffi::load_libraries!(&lua, &[pluto_ffi::PlutoLibrary::Base64])?;
57+
```
4458

4559
## Updating Pluto
4660

patch.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
# Unfortunately, the Pluto author refused to change the hook ABIs to the C ABI (with a fair reason),
2+
# which means we'll have to patch it ourselves. See: https://github.com/PlutoLang/Pluto/issues/1107
13
git apply --ignore-whitespace --whitespace=fix patches/hook_c_abi.patch

patches/libwrapper.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include "lua.hpp"
2+
3+
bool plutow_wants_lib(const char** libs, int num_libs, const char* lib) {
4+
if (libs == nullptr || num_libs == 0) return true;
5+
for (int i = 0; i < num_libs; ++i) {
6+
if (strcmp(libs[i], lib) == 0) {
7+
return true;
8+
}
9+
}
10+
return false;
11+
}
12+
13+
// Source: https://github.com/PlutoLang/Pluto/blob/main/src/linit.cpp#L64
14+
LUALIB_API void plutow_openlibs(lua_State* L, const char** libs, int num_libs) {
15+
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
16+
for (const Pluto::PreloadedLibrary* lib : Pluto::all_preloaded) {
17+
if (!plutow_wants_lib(libs, num_libs, lib->name)) continue;
18+
lua_pushcfunction(L, lib->init);
19+
lua_setfield(L, -2, lib->name);
20+
}
21+
lua_pop(L, 1);
22+
23+
#ifndef PLUTO_DONT_LOAD_ANY_STANDARD_LIBRARY_CODE_WRITTEN_IN_PLUTO
24+
const auto startup_code = R"EOC(
25+
pluto_use "0.6.0"
26+
27+
class exception
28+
__name = "pluto:exception"
29+
30+
function __construct(public what)
31+
local caller
32+
local i = 2
33+
while true do
34+
caller = debug.getinfo(i)
35+
if caller == nil then
36+
error("exception instances must be created with 'pluto_new'", 0)
37+
end
38+
++i
39+
if caller.name == "Pluto_operator_new" then
40+
caller = debug.getinfo(i)
41+
break
42+
end
43+
end
44+
self.where = $"{caller.short_src}:{caller.currentline}"
45+
error(self, 0)
46+
end
47+
48+
function __tostring()
49+
return $"{self.where}: {tostring(self.what)}"
50+
end
51+
end
52+
53+
function instanceof(a, b)
54+
return a instanceof b
55+
end
56+
)EOC";
57+
luaL_loadbuffer(L, startup_code, strlen(startup_code), "Pluto Standard Library");
58+
lua_call(L, 0, 0);
59+
#endif
60+
}

patches/libwrapper.hpp

Lines changed: 0 additions & 5 deletions
This file was deleted.

pluto-ffi/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[package]
2+
name = "pluto-ffi"
3+
version = "0.1.0"
4+
edition = "2021"

pluto-ffi/src/lib.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Due to the way linking works in Rust (dependencies before build script),
2+
// we have to use a macro to do all of this inside of the crate itself.
3+
4+
/// Load all or the specified Pluto libraries into the given Lua state.
5+
/// This should not be called more than once and is inherently unsafe.
6+
#[macro_export]
7+
macro_rules! load_libraries {
8+
($lua:expr, $libs:expr) => {unsafe {
9+
$crate::load_libraries!(@ffi_import);
10+
let libs: &[$crate::PlutoLibrary] = $libs;
11+
let libs = libs.iter().map(|lib| lib.ffi_name()).collect::<Vec<_>>();
12+
let libs = libs.iter().map(|lib| lib.as_ptr()).collect::<Vec<_>>();
13+
$lua.exec_raw::<()>((), |state| plutow_openlibs(state, libs.as_ptr(), libs.len() as _))
14+
}};
15+
($lua:expr) => {unsafe {
16+
$crate::load_libraries!(@ffi_import);
17+
$lua.exec_raw::<()>((), |state| plutow_openlibs(state, std::ptr::null(), 0))
18+
}};
19+
(@ffi_import) => {
20+
extern "C" {
21+
#[allow(non_camel_case_types, non_snake_case)]
22+
pub fn plutow_openlibs(
23+
L: *mut mlua::ffi::lua_State,
24+
libs: *const *const std::os::raw::c_char,
25+
num_libs: std::os::raw::c_int,
26+
);
27+
}
28+
};
29+
}
30+
31+
/// PlutoLibrary defines the available Pluto libraries that can be loaded.
32+
pub enum PlutoLibrary {
33+
Crypto,
34+
Json,
35+
Base32,
36+
Base64,
37+
Assert,
38+
Vector3,
39+
Url,
40+
Star,
41+
Cat,
42+
Http,
43+
Scheduler,
44+
Socket,
45+
BigInt,
46+
Xml,
47+
Regex,
48+
FFI,
49+
Canvas,
50+
}
51+
52+
impl PlutoLibrary {
53+
pub fn ffi_name(&self) -> std::ffi::CString {
54+
std::ffi::CString::new(match self {
55+
Self::Crypto => "crypto",
56+
Self::Json => "json",
57+
Self::Base32 => "base32",
58+
Self::Base64 => "base64",
59+
Self::Assert => "assert",
60+
Self::Vector3 => "vector3",
61+
Self::Url => "url",
62+
Self::Star => "*",
63+
Self::Cat => "cat",
64+
Self::Http => "http",
65+
Self::Scheduler => "scheduler",
66+
Self::Socket => "socket",
67+
Self::BigInt => "bigint",
68+
Self::Xml => "xml",
69+
Self::Regex => "regex",
70+
Self::FFI => "ffi",
71+
Self::Canvas => "canvas",
72+
})
73+
.unwrap()
74+
}
75+
}

src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ impl Build {
7979

8080
// update the package version in Cargo.toml to match the Pluto version!
8181
let (_, version) = env!("CARGO_PKG_VERSION").split_once('-').unwrap();
82-
let src_dir = std::path::PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap();
83-
let src_dir = src_dir.join(format!("Pluto-{version}/src"));
82+
let root_dir = std::path::PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap();
83+
let src_dir = root_dir.join(format!("Pluto-{version}/src"));
8484

8585
build_soup_dependencies(&build, &src_dir);
8686

@@ -115,14 +115,14 @@ impl Build {
115115
build.file(src_dir.join(file).with_extension("cpp"));
116116
}
117117

118+
let patches_dir = root_dir.join("patches");
119+
build.add_files_by_ext(&patches_dir, "cpp");
120+
118121
Self(build)
119122
}
120123

121124
pub fn compile(&mut self) {
122125
self.0.compile("plutostatic");
123-
let out_dir = std::env::var("OUT_DIR").unwrap();
124-
println!("cargo:rustc-link-search=native={out_dir}");
125-
println!("cargo:rustc-link-lib=static=plutostatic");
126126
}
127127
}
128128

testcrate/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55
publish = false
66

77
[dependencies]
8+
pluto-ffi = { path = "../pluto-ffi" }
89
mlua = { version = "0.10", features = [
910
"lua54",
1011
"macros",

0 commit comments

Comments
 (0)