Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move dylib documentation closer to "Embedding Steel in Rust" section. #143

Merged
merged 11 commits into from
Feb 1, 2024
89 changes: 44 additions & 45 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,55 @@

1. [Steel](about/about.md)
1. [Features](about/features.md)
2. [Licensing](about/license.md)
2. [Getting Started](start/start.md)
1. [Licensing](about/license.md)
1. [Getting Started](start/start.md)
1. [Online Playground](start/playground.md)
2. [Using Steel on its own](start/standalone.md)
3. [Using Steel as an embedded scripting engine](start/embedded.md)
3. [The Engine API](engine/engine.md)
1. [Using Steel on its own](start/standalone.md)
1. [Using Rust in Steel](start/dylib.md)
1. [Embedding Steel in Rust](start/embedded.md)
1. [The Rust Engine API](engine/engine.md)
1. [Registering functions](engine/register_function.md)
2. [Embedding values](engine/embedding_values.md)
4. [Language Reference](reference/language.md)
1. [Embedding values](engine/embedding_values.md)
1. [Language Reference](reference/language.md)
1. [Keywords](reference/keywords.md)
2. [Syntax](reference/syntax.md)
3. [Macros](reference/macros.md)
4. [Contracts](reference/contracts.md)
5. [Transducers](reference/transducers.md)
6. [Modules](reference/modules.md)
7. [Functions](reference/functions.md)
1. [Syntax](reference/syntax.md)
1. [Macros](reference/macros.md)
1. [Contracts](reference/contracts.md)
1. [Transducers](reference/transducers.md)
1. [Modules](reference/modules.md)
1. [Functions](reference/functions.md)
1. [Logging](generated/logging.md)
2. [Built ins](reference/builtins.md)
1. [Built ins](reference/builtins.md)
1. [steel/base](builtins/steel_base.md)
2. [steel/constants](builtins/steel_constants.md)
3. [steel/core/option](builtins/steel_core_option.md)
4. [steel/core/result](builtins/steel_core_result.md)
5. [steel/core/types](builtins/steel_core_types.md)
6. [steel/equality](builtins/steel_equality.md)
7. [steel/filesystem](builtins/steel_filesystem.md)
8. [steel/hash](builtins/steel_hash.md)
9. [steel/identity](builtins/steel_identity.md)
10. [steel/io](builtins/steel_io.md)
11. [steel/json](builtins/steel_json.md)
12. [steel/lists](builtins/steel_lists.md)
13. [steel/meta](builtins/steel_meta.md)
14. [steel/numbers](builtins/steel_numbers.md)
15. [steel/ord](builtins/steel_ord.md)
16. [steel/ports](builtins/steel_ports.md)
17. [steel/process](builtins/steel_process.md)
18. [steel/random](builtins/steel_random.md)
19. [steel/sets](builtins/steel_sets.md)
20. [steel/streams](builtins/steel_streams.md)
21. [steel/strings](builtins/steel_strings.md)
22. [steel/symbols](builtins/steel_symbols.md)
23. [steel/syntax](builtins/steel_syntax.md)
24. [steel/time](builtins/steel_time.md)
25. [steel/transducers](builtins/steel_transducers.md)
26. [steel/vectors](builtins/steel_vectors.md)
5. [Dylibs](dylibs/dylibs.md)
1. [Creating a simple shared dylib](dylibs/example.md)
6. [Bytecode](bytecode/bytecode.md)
1. [steel/constants](builtins/steel_constants.md)
1. [steel/core/option](builtins/steel_core_option.md)
1. [steel/core/result](builtins/steel_core_result.md)
1. [steel/core/types](builtins/steel_core_types.md)
1. [steel/equality](builtins/steel_equality.md)
1. [steel/filesystem](builtins/steel_filesystem.md)
1. [steel/hash](builtins/steel_hash.md)
1. [steel/identity](builtins/steel_identity.md)
1. [steel/io](builtins/steel_io.md)
1. [steel/json](builtins/steel_json.md)
1. [steel/lists](builtins/steel_lists.md)
1. [steel/meta](builtins/steel_meta.md)
1. [steel/numbers](builtins/steel_numbers.md)
1. [steel/ord](builtins/steel_ord.md)
1. [steel/ports](builtins/steel_ports.md)
1. [steel/process](builtins/steel_process.md)
1. [steel/random](builtins/steel_random.md)
1. [steel/sets](builtins/steel_sets.md)
1. [steel/streams](builtins/steel_streams.md)
1. [steel/strings](builtins/steel_strings.md)
1. [steel/symbols](builtins/steel_symbols.md)
1. [steel/syntax](builtins/steel_syntax.md)
1. [steel/time](builtins/steel_time.md)
1. [steel/transducers](builtins/steel_transducers.md)
1. [steel/vectors](builtins/steel_vectors.md)
1. [Bytecode](bytecode/bytecode.md)
1. [Optimizations](bytecode/optimizations.md)
7. [Benchmarks](benchmarks/benchmarks.md)
8. [Common Patterns](patterns/patterns.md)
1. [Benchmarks](benchmarks/benchmarks.md)
1. [Common Patterns](patterns/patterns.md)
1. [Interior Mutability](patterns/mutability.md)
2. [Async](patterns/async.md)
9. [Memory Management](garbage-collection/gc.md)
1. [Memory Management](garbage-collection/gc.md)
Empty file removed docs/src/dylibs/dylibs.md
Empty file.
65 changes: 49 additions & 16 deletions docs/src/dylibs/example.md → docs/src/start/dylib.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
# Making a new dynamic library
# Making a Steel Module in Rust

In this guide, we'll make a new shared library that will wrap the `sys-info` crate.
Sometimes you may want to create a Steel module by calling Rust. This can be
useful to:

- Optimize a performance critical codepath with Rust.
- Take advantage of Rust's rich ecosystem.

## Getting started
Whatever the case, Steel provides facilities to create reusable modules based on
Rust code. This process involves compiling Rust into a `dylib`.

## Guide

In this guide, we'll make a `dylib` that will wrap the `sys-info` crate. There
are roughly 3 steps:

1. Create a new Rust library of type "cdylib".
1. Define a module and register types and functions to it.
1. Build the Steel crate and install it to `$STEEL_HOME/native`.
1. Use the library from Steel with `#%require-dylib`.

### Creating a cdylib library

To start, create a new library using `cargo`:

Expand All @@ -19,34 +35,40 @@ This should create a directory structure as follows:
│   └── lib.rs
```

We'll want to make this a `cdylib` library, so we'll adjust the `Cargo.toml` as follows:
We'll want to make this a `cdylib` library for Steel, so we'll perform the following adjustments in `Cargo.toml`:

1. Set the `crate-type` to `"cdylib"`.
1. Include `steel-core` with the `dylibs` feature as a dependency.
1. Include `abi_stable` as a dependency. This is required by some `steel-core`
macros.

```toml
[package]
name = "steel-sys-info"
version.workspace = true
edition = "2021"


[lib]
name = "steel_sys_info"
crate-type = ["cdylib"]

wmedrano marked this conversation as resolved.
Show resolved Hide resolved
[dependencies]
# I'm running this example based on the `steel-sys-info` library found in the steel repo. If you're
# running this on your own, use whichever steel version you'd like to target and pin to that.
steel-core = { workspace = true }
steel-core = { workspace = true, features = ["dylibs"] }
abi_stable = "0.11.1"
sys-info = "0.9.1"
```
wmedrano marked this conversation as resolved.
Show resolved Hide resolved
wmedrano marked this conversation as resolved.
Show resolved Hide resolved

This means that when we run `cargo build` we'll produce a `.so` library that we can then load from `steel`.
This means that when we run `cargo build` we'll produce a shared library (`.so`
file). The shared library can be loaded into other programs, Steel in our case.

## Creating a module

For the purposes of this example, we'll create a module that wraps the `MemInfo` struct, and expose
the information there. Since we'll be implementing traits that are defined inside the `steel` crate, we'll
need to create a struct to wrap the `sys_info::MemInfo` struct:
For the purposes of this example, we'll create a module that wraps the `MemInfo`
struct, and expose the information there. Since we'll be implementing traits
that are defined inside the `steel` crate, we'll need to create a struct to wrap
the `sys_info::MemInfo` struct:

```rust,noplaypen
struct MemoryInfo {
Expand Down Expand Up @@ -84,7 +106,8 @@ impl MemoryInfo {
}
```

Now that we've done that, we can expose this to steel by implementing the `Custom` type for the struct, and declaring an `FFIModule`:
Now that we've done that, we can expose this to steel by implementing the
`Custom` type for the struct, and declaring an `FFIModule`:

```rust,noplaypen
// Using ABI Stable types is very important
Expand Down Expand Up @@ -116,15 +139,19 @@ fn create_module() -> FFIModule {

module
}

```

The `register_fn` API will perform all of the necessary coercions necessary to make this as safe as possible. At the end of the day, this is FFI and we are loading shared libraries, so there is some unsafe Rust code, however steel uses the underlying `abi_stable` library in order to make interactions with the shared library as safe as possible.
The `register_fn` API will perform all of the necessary coercions necessary to
make this as safe as possible. At the end of the day, this is FFI and we are
loading shared libraries, so there is some unsafe Rust code, however steel uses
the underlying `abi_stable` library in order to make interactions with the
shared library as safe as possible.


### Installing the library

To install the dylib in a location where the `steel` interpreter will find it, from the root of the library just run:
To install the dylib in a location where the `steel` interpreter will find it,
from the root of the library just run:

```
$ cargo steel-lib
Expand All @@ -134,7 +161,12 @@ This will build the crate, and copy the resulting dylib to `$STEEL_HOME/native`.

### Using the library from Steel

To load the library, use the syntax `#%require-dylib` - This operates similary to a standard `require`, in that all of the modifiers you're used to using work, such as `only-in` and `prefix-in`. However, the argument is no longer the path to the library, but rather the name of the library without the extension. By default, the library will be named the `[lib]` name used in the toml, prefixed with `lib`.
To load the library, use the syntax `#%require-dylib` - This operates similary
to a standard `require`, in that all of the modifiers you're used to using work,
such as `only-in` and `prefix-in`. However, the argument is no longer the path
to the library, but rather the name of the library without the extension. By
default, the library will be named the `[lib]` name used in the toml, prefixed
with `lib`.

```scheme
(#%require-dylib "libsteel_sys_info"
Expand All @@ -157,4 +189,5 @@ To load the library, use the syntax `#%require-dylib` - This operates similary t

```

This can then be installed as a library itself on the machine, and required just like any other library, using a `cog.scm` file for the manifest.
This can then be installed as a library itself on the machine, and required just
like any other library, using a `cog.scm` file for the manifest.
46 changes: 46 additions & 0 deletions docs/src/start/playground.md
Original file line number Diff line number Diff line change
@@ -1 +1,47 @@
# Online Playground

The Steel Playground allows you to try Steel out directly from your browser. Visit the Steel Playground at:

<https://mattwparas.github.io/steel-playground/dev/>

The Steel Playground's environment is as follows:

- Builtin modules are supported and are automatically imported.
- Dylibs are not supported.

## Output

The output prints the results of all expressions. Additional
information can be printed out with the `display` and `displayln`
functions.

## Bytecode

Bytecode renders the Bytecode that Steel generates from the Steel
code. The Bytecode is a low level representation of the code that is
executed by the Steel interpretter.

## Raw AST

Raw AST exposes the parsed AST. This expands some macros. For example:

```scheme
(define (foo bar)
(+ bar bar))

(define baz '(1 2 3))
```

is actually shorthand for

```scheme
(define foo
(λ (bar)
(+ bar bar)))

(define baz (quote 1 2 3))
```

## Expanded AST

This is similar to the Raw AST but provides more detailed information.
Loading