Giter Club home page Giter Club logo

bun-ffi-gen's Introduction

NPM Version GitHub stars

bun-ffi-gen

This set of tools used for FFI bindings generation for Bun.
Parser could be used for anything actually.

Generates from .h C file.

Currently latest Bun & TypeScript version should be used.

Will exec clang to get ast and type infos, so it should be available in PATH.

Feel free to write an issue with your header file, so I could tweak this package.

Install

bun install bun-ffi-gen

Usage

Example of generating bindings for wgpu library.

import { ClangTypeInfoCache, clangGetAstJson, CodeGen, parseClangAst, clangClean, addIncludeDir } from "bun-ffi-gen";
import path from "path";

const HEADER_PATH = "./wgpu/wgpu.h";
const TYPE_CACHE_PATH = "./generate-wgpu-bindings_cache";

// add include dirs for clang
addIncludeDir(path.resolve("my_include_dir"));

// get header ast from clang
const wgpuAst = clangGetAstJson(HEADER_PATH);

// create clang types cache (for sizeof / offsetof)
const clangTypeInfoCache = await ClangTypeInfoCache.create(TYPE_CACHE_PATH);

// parse ast
const result = parseClangAst(wgpuAst, HEADER_PATH, clangTypeInfoCache);

// update clang cache
await clangTypeInfoCache.save();

// prepare code generation
const codeGen = new CodeGen({
    // see more options below
    funcSymbolsImportLibPathCode(out) {
        out.push(`
            let _LIB_PATH: string = "";

            if (process.platform == "darwin") {
                _LIB_PATH =
                    import.meta.dir +
                    "/../wgpu/libwgpu_native.dylib";
            } else {
                throw new Error("not supported wgpu bindings platform");
            }
        `);

        return "_LIB_PATH";
    },
});

codeGen.generateAll(result);

if (codeGen.failedSymbols.size) {
    console.log("ffi failed for:");
    console.log(Array.from(codeGen.failedSymbols));
}

// write output
codeGen.writeToFile("./wgpu.ts");

// cleanup
await clangTypeInfoCache.save();
await clangClean();

CodeGen options

Passed to constructor, all options are optional.

{
    // tab width (default 4)
    identWidth: number;

    // generate read_* code (default true)
    readers: boolean;

    // generate write_* code (default true)
    writers: boolean;

    // generate helpers code (default true)
    helpers: boolean;

    // generate types for func declarations (default false)
    // may overlap funcWrappers so dont use it untill you have manual bindings
    funcDeclTypes: boolean;

    // generate wrappers around imported func (default true)
    funcWrappers: boolean;

    // generate dlopen import code (default true)
    funcSymbolsImport: boolean;

    // some code that specifies library path for dlopen
    // overwrites `funcSymbolsImportLibPath`
    // should return variable name that contains library path
    // see example
    funcSymbolsImportLibPathCode: (out: string[]) => string;

    // library path, when you dont use `funcSymbolsImportLibPathCode`
    // may be smth like `import.meta.dir + "/mylib"`
    // bun's suffix will be appended
    funcSymbolsImportLibPath: string;

    // throw when smth fails (default false)
    // when false, prints log
    throwOnErrors: boolean;

    // generate STRUCT_NAME__ffi_size constants (default false)
    structSizes: boolean;

    // generate alloc_* code (default true)
    structAllocs: boolean;
}

Generated bindings

For example we have smth like this in .h file:

typedef struct WGPUSurfaceImpl *WGPUSurface;

typedef struct WGPUSurfaceConfiguration {
    WGPUChainedStruct const * nextInChain;
    WGPUDevice device;
    WGPUTextureFormat format;
    WGPUTextureUsageFlags usage;
    size_t viewFormatCount;
    WGPUTextureFormat const * viewFormats;
    WGPUCompositeAlphaMode alphaMode;
    uint32_t width;
    uint32_t height;
    WGPUPresentMode presentMode;
} WGPUSurfaceConfiguration WGPU_STRUCTURE_ATTRIBUTE;

WGPU_EXPORT void wgpuSurfaceConfigure(WGPUSurface surface, WGPUSurfaceConfiguration const * config) WGPU_FUNCTION_ATTRIBUTE;

This will be produced:

type WGPUSurface = Pointer;
const read_WGPUSurface: (from: BunPointer, offset: number) => Pointer;
const write_WGPUSurface: (x: Pointer | TypedArrayPtr<any>, buffer: Buffer, offset: number) => void;

type WGPUSurfaceConfiguration = {
    nextInChain: ConstPtrT<WGPUChainedStruct>;
    device: WGPUDevice;
    format: WGPUTextureFormat;
    usage: WGPUTextureUsageFlags;
    viewFormatCount: size_t;
    viewFormats: ConstPtrT<WGPUTextureFormat>;
    alphaMode: WGPUCompositeAlphaMode;
    width: uint32_t;
    height: uint32_t;
    presentMode: WGPUPresentMode;
};
function read_WGPUSurfaceConfiguration(from: BunPointer, offset: number): WGPUSurfaceConfiguration;
function write_WGPUSurfaceConfiguration(data, buffer: Buffer, offset: number): void;
function alloc_WGPUSurfaceConfiguration(data, buffer?: Buffer): TypedArrayPtr<WGPUSurfaceConfiguration>;

function wgpuSurfaceConfigure(surface, config): void;

const bunImportedLib = dlopen(...);

Then you could do this things:

wgpuSurfaceConfigure(surface, {
    // all other fields are optional
    format: WGPUTextureFormat.WGPUTextureFormat_R16Sint,
});

// or allocate buffer manually
// by default all non specified fields will be 0
const buf = alloc_WGPUSurfaceConfiguration({});
wgpuSurfaceConfigure(surface, buf);

Helpers

// allocates zeroed string
alloc_CString(str: string): BunCString;

// in case when you want to set some pointer to null
const NULL;

// when you want to read array of items from binary
// you could get cTypeSize from *__ffi_size constants when set structSizes=true in CodeGen
// itemReader is one of read_* funcs
bunReadArray(from, offset, cTypeSize, itemReader);

bun-ffi-gen's People

Contributors

morglod avatar recanman avatar

Stargazers

Daniel avatar  avatar Belmondo avatar Valentyn avatar  avatar Daniel Grondin avatar Evan Sarmiento avatar  avatar Michael Demarais avatar  avatar Lorenzo Mangani avatar

Watchers

 avatar

bun-ffi-gen's Issues

clang11 problem

I tried the example given in the readme with the latest version of wgpu-native for the github page for it, it seems to create an error with one of the structs and tries to run c code containing sizeof(undefined). not sure how to really fix this

Issues with libvirt.h

I am using libvirt.h

Code I used (almost identical to the example code):

import { ClangTypeInfoCache, clangGetAstJson, CodeGen, parseClangAst, clangClean } from "bun-ffi-gen";

const HEADER_PATH = "libvirt.h";
const TYPE_CACHE_PATH = "./bindings_cache";

const ast = clangGetAstJson(HEADER_PATH);
const clangTypeInfoCache = await ClangTypeInfoCache.create(TYPE_CACHE_PATH);
const result = parseClangAst(ast, HEADER_PATH, clangTypeInfoCache);

await clangTypeInfoCache.save();

const codeGen = new CodeGen();

codeGen.generateAll(result);

if (codeGen.failedSymbols.size) {
    console.log("ffi failed for:");
    console.log(Array.from(codeGen.failedSymbols));
}

// write output
codeGen.writeToFile("./libvirt.ts");

// cleanup
await clangTypeInfoCache.save();
await clangClean();

I ran it in Bun's alpine Docker image, and I got the following error:

sizeof cache not found, it may take some time
offsetof cache not found, it may take some time
overwrite decl name= undefined item= {
  type: "enum",
  size: 4,
  name: undefined,
  fields: Map(7) {
    "VIR_TYPED_PARAM_INT": {
      type: "int",
      value: "1",
    },
    "VIR_TYPED_PARAM_UINT": {
      type: "int",
      value: "2",
    },
    "VIR_TYPED_PARAM_LLONG": {
      type: "int",
      value: "3",
    },
    "VIR_TYPED_PARAM_ULLONG": {
      type: "int",
      value: "4",
    },
    "VIR_TYPED_PARAM_DOUBLE": {
      type: "int",
      value: "5",
    },
    "VIR_TYPED_PARAM_BOOLEAN": {
      type: "int",
      value: "6",
    },
    "VIR_TYPED_PARAM_STRING": {
      type: "int",
      value: "7",
    },
  },
}
overwrite decl name= undefined item= {
  type: "enum",
  size: 4,
  name: undefined,
  fields: Map(1) {
    "VIR_TYPED_PARAM_STRING_OKAY": {
      type: "expr",
      value: "(1 << 2)",
    },
  },
}
<stdin>:6:38: error: use of undeclared identifier '_virTypedParameter'
            printf("[ %lu ]", sizeof(_virTypedParameter));
                                     ^
1 error generated.
 6 |     const result = execSync(cmd).toString();
 7 |     return JSON.parse(result).inner;
 8 | }
 9 | 
10 | export function clangCompileAndRunReadOut(code: string) {
11 |     execSync("clang -x c - -o ./generate-bindings_tmp_exec", {
         ^
error: <stdin>:6:38: error: use of undeclared identifier '_virTypedParameter'
            printf("[ %lu ]", sizeof(_virTypedParameter));
                                     ^
1 error generated.

   errno: -1
   code: "1"
 syscall: "spawnSync"
   path: "/bin/sh"

      at new SystemError (node:child_process:823:27)
      at node:child_process:175:47
      at node:child_process:224:45
      at clangCompileAndRunReadOut (/usr/src/app/node_modules/bun-ffi-gen/src/clang.ts:11:5)
      at clangGetSizeOf (/usr/src/app/node_modules/bun-ffi-gen/src/clang.ts:71:31)
      at parseClangAst (/usr/src/app/node_modules/bun-ffi-gen/src/parser.ts:260:32)
      at /usr/src/app/index.ts:8:16

Dockerfile:

FROM oven/bun:alpine as base
WORKDIR /usr/src/app
COPY . .

RUN apk add --no-cache clang musl-dev libvirt-dev && bun install && bun run index.ts

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.