jlrs

repository·master·Indexed 19 days ago

https://github.com/taaitaaiger/jlrs

A Rust interface for Julia providing low-level bindings and utilities to interact with the Julia runtime. It includes crates for raw FFI bindings (jl-sys), low-level extensions (jlrs-sys), and tools for detecting Julia installations (find-julia) and verifying bindings (ffi-validator). The library supports various runtimes (local-rt, async-rt, tokio-rt, multi-rt) and provides the julia_module! macro to export Rust functions, types, and constants as a Julia module.

Tokens
16.8K
Snippets
50
Records
74
Agent score
66%

What's inside jlrs

  1. Understand the FFI crates in jlrs

    master

    The jlrs project utilizes two distinct Foreign Function Interface (FFI) crates to interact with Julia:

    1. jl-sys: Provides direct bindings to libjulia.
    2. jlrs-sys: Provides low-level extensions to the standard Julia bindings. These extensions are primarily implemented in C and include:
      • Accessors for specific struct fields.
      • Wrappers for inline functions and functional macros.
      • Trampoline functions to enable advanced features like try-catch blocks and non-statically-sized GC frames.

    jlrs-sys is compiled as a static library to facilitate fast Thread Local Storage (TLS) access and to enable cross-language Link Time Optimization (LTO).

  2. Use jlrs-sys for low-level Julia extensions

    master

    The jlrs-sys crate provides low-level extensions to libjulia that are not available in the standard Rust bindings. It is intended for developers who need fine-grained control over the Julia runtime from Rust.

    Key capabilities provided by jlrs-sys include:

    • Implementing Try/catch blocks
    • Managing GC (Garbage Collector) frames
    • Accessing Fast TLS (Thread Local Storage)
    • Accessing specific struct field accessors
    • Using wrappers for Julia functional macros and static inline functions
  3. Use jl-sys for raw Julia C API bindings

    master
    The jl-sys crate provides low-level, raw FFI (Foreign Function Interface) bindings to the Julia C API. It is intended for use when you need direct access to Julia's C functions without the high-level abstractions provided by the main jlrs crate. Use this crate if you are building custom FFI logic or require specific C-level interactions with the Julia runtime.
  4. Configure jlrs Runtimes

    master

    Runtimes allow you to initialize Julia from within a Rust application.

    WARNING: Runtime features must only be enabled by applications that embed Julia. Libraries must never enable a runtime feature.

    Available Runtimes

    • local-rt: Provides single-threaded, blocking access to Julia.
    • async-rt: Runs Julia on a separate thread and can be used from multiple threads.
    • tokio-rt: Provides a tokio-based executor for the async-rt.
    • multi-rt: Enables calling Julia from arbitrary threads. When combined with async-rt, it creates Julia-aware thread pools.

    Performance Optimization

    When building an application that embeds Julia, set the following environment variable to ensure fast code execution:

    RUSTFLAGS="-Clink-arg=-rdynamic"
  5. Understand the supported Julia version range in jlrs-compat

    master
    The jlrs-compat crate defines the range of Julia versions that are officially supported by jlrs, as well as the current stable Julia version. This is used to ensure compatibility between the Rust bindings and the Julia runtime. Note that jlrs documentation is currently always built against the stable version of Julia.
  6. Install and configure jlrs with juliaup

    master
    If you use juliaup to manage Julia versions, the default detection mechanism in jlrs will not work. To resolve this, you must install the jlrs-launcher application. This tool uses the juliaup crate to correctly identify Julia's location, headers, and libraries, and launches your application with the necessary updated environment.
  7. Build and use ccall examples with Julia

    master

    The ccall and ccall_throw_exceptions examples are libraries designed to be called from within Julia. To use them, follow these steps:

    1. Build the libraries:
      cargo build --example $name
    
    2. **Configure the environment**: Add the build output directory to your system's library path so Julia can locate the compiled libraries.
       - **Linux**: Add `$REPO_ROOT/target/debug/examples` to the `LD_LIBRARY_PATH` environment variable.
       - **Windows**: Add `$REPO_ROOT/target/debug/examples` to the `PATH` environment variable.
    
    3. **Execute from Julia**:
       Run your Julia script using the compiled example name:
       ```bash
    julia $name.jl
    cargo build --example $name
    # Then set LD_LIBRARY_PATH or PATH
    # and run:
    julia $name.jl
  8. Install and configure jlrs for Julia (without juliaup)

    master

    The recommended way to use jlrs is to install Julia via official binaries. jlrs attempts to detect Julia automatically by running which julia (Linux/macOS) or where julia (Windows).

    Manual Configuration via JLRS_JULIA_DIR

    If automatic detection fails or you need to use a specific Julia installation, set the JLRS_JULIA_DIR environment variable to the root directory of your Julia installation (the directory containing the bin folder).

    • Linux: jlrs looks for headers at $JLRS_JULIA_DIR/include/julia/julia.h and the library at $JLRS_JULIA_DIR/lib/libjulia.so.
    • macOS: Uses the same paths as Linux.
    • Windows: jlrs looks for the executable via where julia. You can override this with JLRS_JULIA_DIR.

    Library Search Path (Linux/macOS)

    To ensure libjulia.so (Linux) or libjulia.dylib (macOS) can be loaded, you must add the Julia library directory to your system's library search path:

    • Linux: Add /path/to/julia-x.y.z/lib to LD_LIBRARY_PATH.
    • macOS: Add /path/to/julia-x.y.z/lib to DYLD_LIBRARY_PATH.

    Windows Setup

    Ensure the Julia bin folder is added to your Path environment variable so that julia.exe is discoverable.

  9. Use the FFI validator to verify bindings

    master

    The ffi-validator is a utility crate used to verify that all globals and functions exported by jl_sys and jlrs_sys exist and possess the correct signatures. It achieves this by generating a C file that performs these checks.

    To use the validator, run the CLI tool providing the paths to the jl_sys and jlrs_sys bindings as arguments.

    ffi-validator [OPTIONS] <JL_SYS_BINDINGS_PATH> <JLRS_SYS_BINDINGS_PATH>
  10. Configure JlrsCore installation via environment variables

    master

    jlrs uses the JlrsCore package in Julia. By default, it installs the latest version automatically. You can control this behavior using the following environment variables:

    VariableDescription
    JLRS_CORE_VERSIONInstalls a specific version (e.g., 0.1.2) before loading.
    JLRS_CORE_REVISIONInstalls a specific revision of JlrsCore.
    JLRS_CORE_REPOSets the repository URL for downloading JlrsCore (used with JLRS_CORE_REVISION).
    JLRS_CORE_NO_INSTALLIf set, prevents jlrs from installing JlrsCore (value is ignored, presence is what matters).

    Precedence: JLRS_CORE_NO_INSTALL > JLRS_CORE_REVISION > JLRS_CORE_VERSION.

  11. Use ValueAccessor for arrays of Julia Values

    master

    For arrays where elements are Julia Values (objects), use ValueAccessor. These accessors handle the atomic nature of Julia values.

    • get(target, index): Returns Option<ValueData<'target, 'data, Tgt>> by loading the atomic reference.
    • as_slice(): Returns a slice of AtomicValueRef<Value<'scope, 'data>>.

    ValueAccessorMut allows for direct manipulation of these values:

    • push(value): Appends a Value to the end of the vector.
    • append(arr): Appends another VectorAny to the end of the vector.
    // Example: Appending to a ValueAccessorMut
    value_accessor_mut.push(new_value);
    value_accessor_mut.append(other_vector_any);