defines (bun)

未關閉
#5 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
2/5
預估耗時
1-3 小時
新手友好度
48/100
Issue 類型
功能
描述清晰度
基本清楚
活躍度
停滯
技術堆疊
bun, javascript
領域
tooling

研究方向

產生器位於 tools/gen_bun.js,define 資料從 api.json 讀取;先檢查 defines 和現有的 COLOR 篩選。完成的標準是,產生的 raylib_bun.js 也會處理非巨集、非色彩的 defines,且產生的輸出反映受支援的 define 資料。

由索引模型根據 Issue 內容生成。

描述

bunjs enhancement todo

We need to handle non-macro, non-color defines.

https://github.com/konsumer/raylib-pointers/blob/f365325/tools/gen_bun.js#L176


// this will generate the bun FFI wrapper

import { writeFile } from 'fs/promises'
import api from './api.json'

const { defines, structs, aliases, enums, callbacks, unwrapped, wrapped, struct_utils } = api

// map the arg/return type to bun ffi syntax
const mapType = (t) => {
  if (t === 'void' || !t) {
    return 'void'
  }
  if (t === 'bool') {
    return 'bool'
  }
  if (t === 'float') {
    return 'f32'
  }
  if (t === 'unsigned char') {
    return 'u8'
  }
  if (t.includes('*')) {
    return 'ptr'
  }
  if (t === 'int') {
    return 'i32'
  }
  if (t === 'unsigned int') {
    return 'u32'
  }
  if (t.includes('[') || t.includes('*') || /^[A-Z]/.test(t)) {
    return 'ptr'
  }
  if (t === 'char') {
    return 'char'
  }
  if (t === 'double') {
    return 'double'
  }
  if (t === 'long') {
    return 'i32'
  }

  console.log('unhandled type', t)

  return ''
}

// convert api-info format to bun FFI-object
function processFunctionFFI(f) {
  const returns = mapType(f.returnType)
  const out = {
    args: f.params.map((p) => mapType(p.type))
  }
  if (returns !== 'void') {
    out.returns = returns
  }
  return out
}

const rlVersion = defines.find((d) => d.name === 'RAYLIB_VERSION').value

const out = [
  `// this uses bun FFI and provides an ergonomic interface for js
// Generated at ${new Date().toISOString()} for raylib ${rlVersion}

import { dlopen, FFIType, suffix, ptr } from 'bun:ffi'

`
]

const ffiInfo = {}

const blocklist = [
  // variadic-params is not supported
  'TraceLog',
  'TextFormat'
]

// TODO: these structs have nested structs, which I have not worked out yet, C-side
const blocklistStructs = ['RenderTexture', 'NPatchInfo', 'GlyphInfo', 'Font', 'Camera3D', 'Camera2D', 'MaterialMap', 'Material', 'Transform', 'BoneInfo', 'Model', 'ModelAnimation', 'Ray', 'RayCollision', 'BoundingBox', 'AudioStream', 'Sound', 'Music', 'VrDeviceInfo', 'VrStereoConfig', 'AutomationEvent', 'AutomationEventList']

for (const f of unwrapped) {
  if (blocklist.includes(f.name)) {
    continue
  }
  ffiInfo[f.name] = processFunctionFFI(f)
}

for (const f of wrapped) {
  if (blocklist.includes(f.name)) {
    continue
  }
  ffiInfo[f.name] = processFunctionFFI(f)
}

for (const f of struct_utils) {
  ffiInfo[f.name] = processFunctionFFI(f)
}

// remove any struct-utils for problem-structs
for (const n of Object.keys(ffiInfo)) {
  for (const t of blocklistStructs) {
    if (n === `${t}_size` || n.startsWith(`${t}_get`) || n.startsWith(`${t}_set`)) {
      delete ffiInfo[n]
    }
  }
}

out.push(`const ffi = ${JSON.stringify(ffiInfo, null, 2)}`)

out.push(`
const { symbols } = dlopen(\`build/librlptr.\${suffix}\`, ffi)

// convert a string into a pointer to a buffer
const cstr = s => ptr(Buffer.from((s || '\0')))

`)

// TODO: this is temporary until I get codegen for functions
out.push(`
export const alloc = symbols.wrapped_alloc
export const free = symbols.wrapped_free
export const WindowShouldClose = symbols.wrapped_WindowShouldClose

// these don't need any wrapping
export const { CloseWindow, BeginDrawing, EndDrawing, SetTargetFPS, DrawFPS } = symbols

// these need a little adjustment
export const ClearBackground = color => symbols.wrapped_ClearBackground(color._addr)

export const DrawText = (text, posX, posY, fontSize, color) => symbols.wrapped_DrawText(cstr(text), posX, posY, fontSize, color._addr)

export const InitWindow = (width, height, title) => symbols.InitWindow(width, height, cstr(title))

// this is example with pointer param/return
export const Fade = (color, alpha) => new Color({}, symbols.wrapped_Fade(color._addr, alpha))
`)

const structWrappers = []
for (const { name, description, fields } of structs) {
  structWrappers.push(`export class ${name} {
  constructor (init = {}, address) {
    this._addr = address || alloc(symbols.${name}_size())
    for (const k of Object.keys(init)) {
      this[k] = init[k]
    }
  }
`)

  for (const field of fields) {
    const ret = mapType(field.type)
    if (ret === 'ptr') {
      // TODO: nested structs are not worked out yet
      continue
    }
    structWrappers.push(`  get ${field.name}() {
    return symbols.${name}_get_${field.name}(this._addr)
  }`)
    structWrappers.push(`  set ${field.name}(v) {
    return symbols.${name}_set_${field.name}(this._addr, v)
  }`)
  }
  structWrappers.push('}')
}
out.push(...structWrappers)

const colors = defines
  .filter((d) => d.type === 'COLOR')
  .map((c) => {
    const m = [...c.value.matchAll(/\{ ([0-9]+), ([0-9]+), ([0-9]+), ([0-9]+) \}/g)]
    return `export const ${c.name} = new Color({r: ${m[0][1]}, g: ${m[0][2]}, b: ${m[0][3]}, a: ${m[0][4]}}) // ${c.description}`
  })
out.push(...colors)

// TODO: handle non-macro, non-color defines

// TODO: add wrapped/unwrapped exports

await writeFile('raylib_bun.js', out.join('\n'))

主要語言
C
星號
0
分支
1
PR 合併指標
30 天內沒有已合併 PR

環境準備

這個專案沒有提供開發容器、Dockerfile 或貢獻指南,環境需要你自己搭建:先看它的 README,通用步驟見我們的新手貢獻指南。

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

konsumer/raylib-pointers 的其他 Issue

查看 konsumer/raylib-pointers 的全部 Issue

相似的 Issue

更多 C Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。