mirror of
https://github.com/extism/extism.git
synced 2026-01-10 06:18:00 -05:00
feat(dlang): Add a D host SDK (#412)
See [dlang.org](https://dlang.org). SDK Coverage: ~91.30% (21/23) ## Issues - [x] These symbols are undefined in the latest release ([v0.5.2](https://github.com/extism/extism/releases/tag/v0.5.2)): > Undefined symbols for architecture x86_64: > "_extism_plugin_id" > "_extism_plugin_new_error_free" Using `libextism` compiled from source does _not_ have this issue. - [ ] `dub lint` reads many intermediate targets files: https://github.com/dlang/dub/issues/2700 ## Todo List - [x] Flesh out initial binding - [ ] Cover host API: - [x] `ExtismFunctionType` See `FunctionType` alias. - [x] `extism_plugin_id` - [x] `extism_plugin_new_error_free` - [ ] `extism_plugin_error` - [ ] Add unit tests - [x] Add usage example --------- Co-authored-by: zach <zachshipko@gmail.com>
This commit is contained in:
36
.github/workflows/ci-d.yml
vendored
Normal file
36
.github/workflows/ci-d.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-d.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- d/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: D CI
|
||||
|
||||
jobs:
|
||||
d:
|
||||
name: D
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
# TODO: Use multiple versions once stable
|
||||
d_version: [ldc-1.33.0]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup D environment
|
||||
uses: dlang-community/setup-dlang@v1
|
||||
with:
|
||||
compiler: ${{ matrix.d_version }}
|
||||
|
||||
- name: Test D Host SDK
|
||||
run: |
|
||||
dub --version
|
||||
LD_LIBRARY_PATH=/usr/local/lib dub test
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,6 +15,8 @@ python/poetry.lock
|
||||
c/main
|
||||
cpp/test/test
|
||||
cpp/example
|
||||
.dub
|
||||
dub.selections.json
|
||||
go/main
|
||||
ruby/.bundle/
|
||||
ruby/.yardoc
|
||||
|
||||
7
d/.gitignore
vendored
Normal file
7
d/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated by host system's C preprocessor
|
||||
# See https://dlang.org/spec/importc.html#manual-cpp
|
||||
runtime.i
|
||||
|
||||
docs.json
|
||||
extism-test-*
|
||||
*.lst
|
||||
42
d/README.md
Normal file
42
d/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Extism D SDK
|
||||
|
||||
D bindings to the Extism host SDK.
|
||||
|
||||
## Development
|
||||
|
||||
1. Ensure a [D compiler](https://dlang.org/download) is installed and available in your `PATH`.
|
||||
> Note: The compiler's frontend must be at least version `2.102`.
|
||||
2. Ensure the extism shared library is installed *before* running tests or examples:
|
||||
|
||||
See [Installing Extism](https://extism.org/docs/install) documentation.
|
||||
|
||||
```sh
|
||||
extism install latest
|
||||
extism link
|
||||
```
|
||||
|
||||
> Note: The `extism install` command may require `sudo`.
|
||||
|
||||
### Lint Sources
|
||||
|
||||
```sh
|
||||
dub lint
|
||||
```
|
||||
|
||||
> Note: Until [dlang/dub#2700](https://github.com/dlang/dub/issues/2700) is fixed, ensure all non-D sources (e.g. intermediate `*.d` files in `targets`) are cleaned from the repo.
|
||||
>
|
||||
> Run `cargo clean` to avoid seeing many invalid lint issues.
|
||||
|
||||
### Running Tests
|
||||
|
||||
```sh
|
||||
dub test
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Hello World
|
||||
|
||||
```sh
|
||||
dub run extism:hello
|
||||
```
|
||||
17
d/examples/hello/.gitignore
vendored
Normal file
17
d/examples/hello/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
.dub
|
||||
docs.json
|
||||
__dummy.html
|
||||
docs/
|
||||
/extism_hello
|
||||
/hello
|
||||
hello.so
|
||||
hello.dylib
|
||||
hello.dll
|
||||
hello.a
|
||||
hello.lib
|
||||
hello-test-*
|
||||
*.exe
|
||||
*.pdb
|
||||
*.o
|
||||
*.obj
|
||||
*.lst
|
||||
15
d/examples/hello/dub.json
Normal file
15
d/examples/hello/dub.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "hello",
|
||||
"description": "A minimal Extism usage example",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
"Chance Snow <git@chancesnow.me>",
|
||||
"Extism contributors"
|
||||
],
|
||||
"copyright": "Copyright © 2023, Extism contributors",
|
||||
"dependencies": {
|
||||
"extism": {
|
||||
"path": "../../../"
|
||||
}
|
||||
}
|
||||
}
|
||||
33
d/examples/hello/source/app.d
Normal file
33
d/examples/hello/source/app.d
Normal file
@@ -0,0 +1,33 @@
|
||||
import std.conv: castFrom, to;
|
||||
import std.file;
|
||||
import std.functional: toDelegate;
|
||||
import std.stdio;
|
||||
import std.string: representation;
|
||||
import std.typecons : Yes;
|
||||
|
||||
import extism;
|
||||
|
||||
void main() {
|
||||
auto wasm = cast(ubyte[]) read("wasm/code-functions.wasm");
|
||||
// FIXME: Creating the plugin results in EXC_BAD_ACCESS (segfault?) from `extism_plugin_new`
|
||||
auto plugin = new Plugin(wasm, [
|
||||
Function!string(
|
||||
"hello_world", /* Inputs */ [ValType.i64], /* Outputs */ [ValType.i64], toDelegate(&helloWorld), "Hello, again!"
|
||||
),
|
||||
], Yes.withWasi);
|
||||
|
||||
auto input = "aeiou";
|
||||
plugin.call("count_vowels", cast(ubyte[]) input.representation);
|
||||
writeln(plugin.outputData);
|
||||
}
|
||||
|
||||
///
|
||||
void helloWorld(CurrentPlugin plugin, const Val[] inputs, Val[] outputs, void *data) {
|
||||
writeln("Hello from D!");
|
||||
writeln(data);
|
||||
|
||||
ExtismSize ptr_offs = inputs[0].v.i64;
|
||||
auto buf = plugin.memory(ptr_offs);
|
||||
writeln(buf);
|
||||
outputs[0].v.i64 = inputs[0].v.i64;
|
||||
}
|
||||
258
d/extism.d
Normal file
258
d/extism.d
Normal file
@@ -0,0 +1,258 @@
|
||||
module extism;
|
||||
|
||||
import std.conv: castFrom, to;
|
||||
import std.meta: Alias;
|
||||
import std.string: fromStringz, toStringz;
|
||||
|
||||
import runtime;
|
||||
|
||||
/// A list of all possible value types in WebAssembly.
|
||||
enum ValType {
|
||||
i32 = I32,
|
||||
i64 = I64,
|
||||
f32 = F32,
|
||||
f64 = F64,
|
||||
v128 = V128,
|
||||
funcRef = FuncRef,
|
||||
externRef = ExternRef,
|
||||
}
|
||||
|
||||
// Opaque Pointers
|
||||
///
|
||||
alias CancelHandle = Alias!(void*);
|
||||
///
|
||||
alias CurrentPlugin = Alias!(void*);
|
||||
|
||||
///
|
||||
alias ExtismSize = ulong;
|
||||
|
||||
/// A union type for host function argument/return values.
|
||||
union ValUnion {
|
||||
///
|
||||
int i32;
|
||||
///
|
||||
long i64;
|
||||
///
|
||||
float f32;
|
||||
///
|
||||
double f64;
|
||||
}
|
||||
|
||||
/// Holds the type and value of a function argument/return.
|
||||
struct Val {
|
||||
///
|
||||
ValType t;
|
||||
///
|
||||
ValUnion v;
|
||||
}
|
||||
|
||||
/// Host function signature.
|
||||
///
|
||||
/// Used to register host functions with plugins.
|
||||
/// Params:
|
||||
/// plugin: the currently executing plugin from within a host function
|
||||
/// inputs: argument values
|
||||
/// outputs: return values
|
||||
/// data: user data associated with the host function
|
||||
alias FunctionType = void delegate(
|
||||
CurrentPlugin plugin, const Val[] inputs, Val[] outputs, void* data
|
||||
);
|
||||
|
||||
/// Returns: A slice of an allocated memory block of the currently running plugin.
|
||||
ubyte[] memory(CurrentPlugin plugin, ulong n) {
|
||||
auto length = extism_current_plugin_memory_length(cast(ExtismCurrentPlugin*) plugin, n);
|
||||
return extism_current_plugin_memory(cast(ExtismCurrentPlugin*) plugin)[n .. (n + length)];
|
||||
}
|
||||
|
||||
/// Allocate a memory block in the currently running plugin.
|
||||
ulong memoryAlloc(CurrentPlugin plugin, ulong n) {
|
||||
return extism_current_plugin_memory_alloc(cast(ExtismCurrentPlugin*) plugin, n);
|
||||
}
|
||||
|
||||
/// Free an allocated memory block.
|
||||
void memoryFree(CurrentPlugin plugin, ulong ptr) {
|
||||
extism_current_plugin_memory_free(cast(ExtismCurrentPlugin*) plugin, ptr);
|
||||
}
|
||||
|
||||
/// A host function, where `T` is the type of its user data.
|
||||
struct Function(T) {
|
||||
/// Managed pointer.
|
||||
ExtismFunction* func;
|
||||
alias func this;
|
||||
private string _namespace = null;
|
||||
|
||||
/// Create a new host function.
|
||||
/// Params:
|
||||
/// name: function name, this should be valid UTF-8
|
||||
/// inputs: argument types
|
||||
/// outputs: return types
|
||||
/// func: the function to call
|
||||
/// userData: a pointer that will be passed to the function when it's called. This value should live as long as the function exists.
|
||||
/// freeUserData: a callback to release the `userData`` value when the resulting `Function` is freed.
|
||||
this(
|
||||
string name, const ValType[] inputs, const ValType[] outputs, FunctionType func,
|
||||
T userData, void function(T userData) freeUserData = null
|
||||
) {
|
||||
import std.functional: toDelegate;
|
||||
|
||||
// Bind the given host function with C linkage
|
||||
auto funcClosure = ((
|
||||
ExtismCurrentPlugin* plugin,
|
||||
const ExtismVal* inputs, ulong numInputs, ExtismVal* outputs, ulong numOutputs,
|
||||
void* data
|
||||
) {
|
||||
func(plugin, (cast(const Val*) inputs)[0 .. numInputs], (cast(Val*) outputs)[0 .. numOutputs], data);
|
||||
}).toDelegate.bindDelegate;
|
||||
|
||||
// Bind the `freeUserData` function with C linkage
|
||||
auto freeUserDataHandler = freeUserData is null ? null : ((void* userDataPtr) {
|
||||
if (userDataPtr is null) return;
|
||||
freeUserData((&userDataPtr).to!T);
|
||||
}).toDelegate.bindDelegate;
|
||||
|
||||
this.func = extism_function_new(
|
||||
name.toStringz,// See https://dlang.org/spec/importc.html#enums
|
||||
// See https://forum.dlang.org/post/qmidcpaxctbuphcyvkdc@forum.dlang.org
|
||||
castFrom!(const(ValType)*)
|
||||
.to!(typeof(ExtismVal.t)*)(inputs.ptr), inputs.length,
|
||||
castFrom!(const(ValType)*).to!(typeof(ExtismVal.t)*)(outputs.ptr), outputs.length,
|
||||
funcClosure,
|
||||
&userData,
|
||||
freeUserDataHandler,
|
||||
);
|
||||
}
|
||||
|
||||
~this() {
|
||||
extism_function_free(func);
|
||||
}
|
||||
|
||||
/// Get the namespace of this function.
|
||||
string namespace() {
|
||||
return this._namespace;
|
||||
}
|
||||
/// Set the namespace of this function.
|
||||
void namespace(string value) {
|
||||
this._namespace = value;
|
||||
extism_function_set_namespace(func, value.toStringz);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
struct Plugin {
|
||||
import std.uuid: UUID;
|
||||
|
||||
/// Managed pointer.
|
||||
ExtismPlugin* plugin;
|
||||
alias plugin this;
|
||||
|
||||
/// Create a new plugin.
|
||||
/// Params:
|
||||
/// wasm: is a WASM module (wat or wasm) or a JSON encoded manifest
|
||||
/// functions: is an array of ExtismFunction*
|
||||
/// withWasi: enables/disables WASI
|
||||
/// Throws: `Exception` if the plugin could not be created.
|
||||
this(const ubyte[] wasm, const(ExtismFunction)*[] functions, bool withWasi = false) {
|
||||
char* errorMsgPtr = null;
|
||||
plugin = extism_plugin_new(
|
||||
wasm.ptr, wasm.length, cast(ExtismFunction**) functions.ptr, functions.length, withWasi, &errorMsgPtr
|
||||
);
|
||||
// See https://github.com/extism/extism/blob/ddcbeec3debe787293a9957c8be88f80a64b7c22/c/main.c#L67
|
||||
// Instead of terminating the host process, throw.
|
||||
if (plugin !is null)
|
||||
return;
|
||||
auto errorMsg = errorMsgPtr.fromStringz.idup;
|
||||
extism_plugin_new_error_free(errorMsgPtr);
|
||||
// TODO: Subclass `Exception` for better error handling
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
|
||||
~this() {
|
||||
extism_plugin_free(plugin);
|
||||
}
|
||||
|
||||
/// Get a plugin's ID.
|
||||
UUID id() {
|
||||
return UUID(extism_plugin_id(plugin)[0 .. 16]);
|
||||
}
|
||||
|
||||
/// Get the error associated with this `Plugin`.
|
||||
string error() {
|
||||
return extism_error(plugin).fromStringz.idup;
|
||||
}
|
||||
|
||||
/// Update plugin config values, this will merge with the existing values.
|
||||
bool config(ubyte[] json) {
|
||||
return extism_plugin_config(plugin, json.ptr, json.length);
|
||||
}
|
||||
|
||||
/// See_Also: `cancel`
|
||||
const(CancelHandle) cancelHandle() {
|
||||
return extism_plugin_cancel_handle(plugin);
|
||||
}
|
||||
|
||||
/// Cancel a running plugin.
|
||||
/// See_Also: `cancelHandle`
|
||||
bool cancel(const CancelHandle handle) {
|
||||
return extism_plugin_cancel(cast(ExtismCancelHandle*) handle);
|
||||
}
|
||||
|
||||
/// Returns: Whether a function with `name` exists.
|
||||
bool functionExists(string name) {
|
||||
return extism_plugin_function_exists(plugin, name.toStringz);
|
||||
}
|
||||
|
||||
/// Call a function.
|
||||
/// Params:
|
||||
/// funcName: is the function to call
|
||||
/// data: is the input data
|
||||
void call(string funcName, ubyte[] data = null) {
|
||||
// Returns `0` if the call was successful, otherwise `-1`.
|
||||
if (extism_plugin_call(plugin, funcName.toStringz, data.ptr, data.length) != 0)
|
||||
throw new Exception(this.error);
|
||||
}
|
||||
|
||||
/// Get the plugin's output data.
|
||||
///
|
||||
/// Note: This copies the data into a managed buffer.
|
||||
/// Remarks: Use `outputData.length` to retrieve size of plugin output.
|
||||
ubyte[] outputData() {
|
||||
import std.algorithm: copy;
|
||||
|
||||
auto outputLength = extism_plugin_output_length(plugin);
|
||||
auto outputData = extism_plugin_output_data(plugin)[0 .. outputLength];
|
||||
auto buffer = new ubyte[outputLength];
|
||||
assert(
|
||||
outputData.copy(buffer).length == 0,
|
||||
"Output data was not completely copied into buffer, i.e. buffer was not filled."
|
||||
);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the error associated with a `Context` or `Plugin`, if plugin is -1 then the context error will be returned.
|
||||
|
||||
/// Set log file and level.
|
||||
bool setLogFile(string filename, string logLevel) {
|
||||
return extism_log_file(filename.toStringz, logLevel.toStringz);
|
||||
}
|
||||
|
||||
/// Get the Extism version string.
|
||||
string version_() {
|
||||
return extism_version().fromStringz.idup;
|
||||
}
|
||||
|
||||
import std.traits: isDelegate;
|
||||
|
||||
/// Transform the given delegate into a static function pointer with C linkage.
|
||||
/// See_Also: <a href="https://stackoverflow.com/a/22845722/1363247">stackoverflow.com/a/22845722/1363247</a>
|
||||
package auto bindDelegate(Func)(Func f) if (isDelegate!Func) {
|
||||
import std.traits: ParameterTypeTuple, ReturnType;
|
||||
|
||||
static Func delegate_;
|
||||
delegate_ = f;
|
||||
extern (C) static ReturnType!Func func(ParameterTypeTuple!Func args) {
|
||||
return delegate_(args);
|
||||
}
|
||||
|
||||
return &func;
|
||||
}
|
||||
1
d/runtime.c
Normal file
1
d/runtime.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "runtime/extism.h"
|
||||
116
dscanner.ini
Normal file
116
dscanner.ini
Normal file
@@ -0,0 +1,116 @@
|
||||
; Configure which static analysis checks are enabled
|
||||
[analysis.config.StaticAnalysisConfig]
|
||||
; Check variable, class, struct, interface, union, and function names against t
|
||||
; he Phobos style guide
|
||||
style_check="enabled"
|
||||
; Check for array literals that cause unnecessary allocation
|
||||
enum_array_literal_check="enabled"
|
||||
; Check for poor exception handling practices
|
||||
exception_check="enabled"
|
||||
; Check for use of the deprecated 'delete' keyword
|
||||
delete_check="enabled"
|
||||
; Check for use of the deprecated floating point operators
|
||||
float_operator_check="enabled"
|
||||
; Check number literals for readability
|
||||
number_style_check="enabled"
|
||||
; Checks that opEquals, opCmp, toHash, and toString are either const, immutable
|
||||
; , or inout.
|
||||
object_const_check="enabled"
|
||||
; Checks for .. expressions where the left side is larger than the right.
|
||||
backwards_range_check="enabled"
|
||||
; Checks for if statements whose 'then' block is the same as the 'else' block
|
||||
if_else_same_check="enabled"
|
||||
; Checks for some problems with constructors
|
||||
constructor_check="enabled"
|
||||
; Checks for unused variables
|
||||
unused_variable_check="enabled"
|
||||
; Checks for unused labels
|
||||
unused_label_check="enabled"
|
||||
; Checks for unused function parameters
|
||||
unused_parameter_check="enabled"
|
||||
; Checks for duplicate attributes
|
||||
duplicate_attribute="enabled"
|
||||
; Checks that opEquals and toHash are both defined or neither are defined
|
||||
opequals_tohash_check="enabled"
|
||||
; Checks for subtraction from .length properties
|
||||
length_subtraction_check="enabled"
|
||||
; Checks for methods or properties whose names conflict with built-in propertie
|
||||
; s
|
||||
builtin_property_names_check="enabled"
|
||||
; Checks for confusing code in inline asm statements
|
||||
asm_style_check="enabled"
|
||||
; Checks for confusing logical operator precedence
|
||||
logical_precedence_check="enabled"
|
||||
; Checks for undocumented public declarations
|
||||
undocumented_declaration_check="enabled"
|
||||
; Checks for poor placement of function attributes
|
||||
function_attribute_check="enabled"
|
||||
; Checks for use of the comma operator
|
||||
comma_expression_check="enabled"
|
||||
; Checks for local imports that are too broad. Only accurate when checking code
|
||||
; used with D versions older than 2.071.0
|
||||
local_import_check="disabled"
|
||||
; Checks for variables that could be declared immutable
|
||||
could_be_immutable_check="enabled"
|
||||
; Checks for redundant expressions in if statements
|
||||
redundant_if_check="enabled"
|
||||
; Checks for redundant parenthesis
|
||||
redundant_parens_check="enabled"
|
||||
; Checks for mismatched argument and parameter names
|
||||
mismatched_args_check="enabled"
|
||||
; Checks for labels with the same name as variables
|
||||
label_var_same_name_check="enabled"
|
||||
; Checks for lines longer than `max_line_length` characters
|
||||
long_line_check="enabled"
|
||||
; The maximum line length used in `long_line_check`.
|
||||
max_line_length="120"
|
||||
; Checks for assignment to auto-ref function parameters
|
||||
auto_ref_assignment_check="enabled"
|
||||
; Checks for incorrect infinite range definitions
|
||||
incorrect_infinite_range_check="enabled"
|
||||
; Checks for asserts that are always true
|
||||
useless_assert_check="enabled"
|
||||
; Check for uses of the old-style alias syntax
|
||||
alias_syntax_check="enabled"
|
||||
; Checks for else if that should be else static if
|
||||
static_if_else_check="enabled"
|
||||
; Check for unclear lambda syntax
|
||||
lambda_return_check="enabled"
|
||||
; Check for auto function without return statement
|
||||
auto_function_check="enabled"
|
||||
; Check for sortedness of imports
|
||||
imports_sortedness="disabled"
|
||||
; Check for explicitly annotated unittests
|
||||
explicitly_annotated_unittests="disabled"
|
||||
; Check for properly documented public functions (Returns, Params)
|
||||
properly_documented_public_functions="disabled"
|
||||
; Check for useless usage of the final attribute
|
||||
final_attribute_check="enabled"
|
||||
; Check for virtual calls in the class constructors
|
||||
vcall_in_ctor="enabled"
|
||||
; Check for useless user defined initializers
|
||||
useless_initializer="disabled"
|
||||
; Check allman brace style
|
||||
allman_braces_check="disabled"
|
||||
; Check for redundant attributes
|
||||
redundant_attributes_check="enabled"
|
||||
; Check public declarations without a documented unittest
|
||||
has_public_example="disabled"
|
||||
; Check for asserts without an explanatory message
|
||||
assert_without_msg="disabled"
|
||||
; Check indent of if constraints
|
||||
if_constraints_indent="disabled"
|
||||
; Check for @trusted applied to a bigger scope than a single function
|
||||
trust_too_much="enabled"
|
||||
; Check for redundant storage classes on variable declarations
|
||||
redundant_storage_classes="enabled"
|
||||
; Check for unused function return values
|
||||
unused_result="enabled"
|
||||
; Enable cyclomatic complexity check
|
||||
cyclomatic_complexity="disabled"
|
||||
; Maximum cyclomatic complexity after which to issue warnings
|
||||
max_cyclomatic_complexity="50"
|
||||
; Check for function bodies on disabled functions
|
||||
body_on_disabled_func_check="enabled"
|
||||
; ModuleFilters for selectively enabling (+std) and disabling (-std.internal) individual checks
|
||||
[analysis.config.ModuleFilters]
|
||||
23
dub.json
Normal file
23
dub.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "extism",
|
||||
"description": "The Universal Plug-in System. Extend anything with WebAssembly (wasm).",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
"Chance Snow <git@chancesnow.me>",
|
||||
"Extism contributors"
|
||||
],
|
||||
"copyright": "Copyright © 2023, Extism contributors",
|
||||
"toolchainRequirements": {
|
||||
"frontend": ">=2.102"
|
||||
},
|
||||
"targetPath": "target",
|
||||
"sourceFiles": ["d/extism.d"],
|
||||
"importPaths": ["d"],
|
||||
"systemDependencies": "extism >= 0.4.0",
|
||||
"targetType": "sourceLibrary",
|
||||
"subPackages": [
|
||||
"d/examples/hello"
|
||||
],
|
||||
"dflags": ["-P-I$EXTISM_PACKAGE_DIR"],
|
||||
"libs": ["extism"]
|
||||
}
|
||||
@@ -51,6 +51,7 @@ if __name__ == '__main__':
|
||||
# Check for missing SDK functions
|
||||
sdks = [
|
||||
Lang('cpp', 'hpp'),
|
||||
Lang('d', 'd'),
|
||||
Lang('go', 'go', path='..'),
|
||||
Lang('haskell', 'hs'),
|
||||
Lang('node', 'ts'),
|
||||
|
||||
Reference in New Issue
Block a user