dbenoit17 / dynamic-ffi

Auto define Racket bindings to C by parsing header files.
Other
29 stars 10 forks source link

`generate-mapped-static-ffi` produces empty mapping #27

Open zyrolasting opened 5 years ago

zyrolasting commented 5 years ago

Given the following program...

#lang racket/base

(require dynamic-ffi/unsafe)

(define vulkan-sdk (bytes->string/utf-8
                    (environment-variables-ref (current-environment-variables)
                                               #"VULKAN_SDK")))

(define (vulkan-path . args)
  (apply build-path vulkan-sdk args))

(define lib-path (vulkan-path "lib/libvulkan"))
(define header-path (vulkan-path "include/vulkan/vulkan.h"))

(module+ main  
  (generate-mapped-static-ffi "vulkan"
                              "dffi-vulkan.rkt"
                              lib-path
                              header-path))

I get this output. I was expecting ffi-obj-map to be non-empty.

#lang racket/base

(require ffi/unsafe)

(provide (except-out
           (all-defined-out)
           ffi-obj-map))

(define vulkan-ffi-lib (ffi-lib "/home/sage/vulkan/1.1.121.1/x86_64/lib/libvulkan"))

(define vulkan-headers (list "/home/sage/vulkan/1.1.121.1/x86_64/include/vulkan/vulkan.h"))

(define (warn-undefined-symbol sym)
  (lambda ()
    (fprintf (current-error-port)
      "warning: vulkan does not contain symbol ~a" sym)))

(define ffi-obj-map
 (make-hash
  (list )))

(define vulkan 
  (case-lambda
    [() ffi-obj-map]
    [(sym)
     (let ([obj (hash-ref ffi-obj-map sym)])
       (if (procedure? obj) (obj) obj))]
    [(sym . args)
     (let ([obj (hash-ref ffi-obj-map sym)])
       (apply obj args))]))
dbenoit17 commented 5 years ago

The dynamic ffi does not parse header files recursively. This is because it would also parse out and create many unneeded ffi-objects for all dependencies, such as libc etc. Are there are nested headers for different subsystems you can include in place of the top level vulcan.h?

I know this is an unfortunate limitation, because many large libraries provide a single top level header. The only other recommendation I have is to perhaps define wrapper functions using define-inline-ffi.

I would also like to find a better solution to this issue. If you have any ideas, please let me know.

zyrolasting commented 5 years ago

@dbenoit17 Ah, ok. That makes sense. Vulkan makes heavy use of include guards for per-platform support, but I see some ways to work with this. Thank you.

I would also like to find a better solution to this issue. If you have any ideas, please let me know.

Yeah, I don't think this has an ideal solution. I was spitballing things like telling dynamic-ffi what specific symbols I would expect, but that had its own problems.

The only general solution I see is difficult: A public Racket interface to the clang AST through your plugin, plus a way to "direct" dynamic-ffi to recurse to only programmer-specified headers. I imagine that's a ways off.

dbenoit17 commented 5 years ago

It is actually pretty trivial to have clang recurse and provide all of the metadata. In reality, clang does recurse through the nested headers, but only reports top-level headers to racket to avoid the overhead of generating the objects. There is already a keword arg buried somewhere in the dynamic-ffi library to turn on all nested declarations.

I think you are on to something with your suggestion to direct the library to include programmer-defined headers. We could probably have the public interfaces of the library take an optional lambda to be used as a filter predicate. Thanks for the idea, I will implement that.

zyrolasting commented 5 years ago

Thanks for the idea, I will implement that.

That's great news, thank you! I'll be happy to verify once it is complete.