ffidb 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +1 -0
  3. data/CHANGES.md +7 -0
  4. data/CREDITS.md +2 -0
  5. data/README.md +201 -0
  6. data/UNLICENSE +24 -0
  7. data/VERSION +1 -0
  8. data/bin/ffidb +387 -0
  9. data/etc/mappings/dart.yaml +35 -0
  10. data/etc/mappings/java.yaml +36 -0
  11. data/etc/mappings/lisp.yaml +35 -0
  12. data/etc/mappings/python.yaml +35 -0
  13. data/etc/mappings/ruby.yaml +35 -0
  14. data/etc/templates/c.erb +46 -0
  15. data/etc/templates/cpp.erb +45 -0
  16. data/etc/templates/dart.erb +64 -0
  17. data/etc/templates/go.erb +50 -0
  18. data/etc/templates/java.erb +56 -0
  19. data/etc/templates/lisp.erb +49 -0
  20. data/etc/templates/python.erb +59 -0
  21. data/etc/templates/ruby.erb +48 -0
  22. data/lib/ffidb.rb +34 -0
  23. data/lib/ffidb/enum.rb +37 -0
  24. data/lib/ffidb/errors.rb +64 -0
  25. data/lib/ffidb/exporter.rb +141 -0
  26. data/lib/ffidb/exporters.rb +28 -0
  27. data/lib/ffidb/exporters/c.rb +52 -0
  28. data/lib/ffidb/exporters/cpp.rb +13 -0
  29. data/lib/ffidb/exporters/csharp.rb +6 -0
  30. data/lib/ffidb/exporters/csv.rb +24 -0
  31. data/lib/ffidb/exporters/dart.rb +60 -0
  32. data/lib/ffidb/exporters/go.rb +16 -0
  33. data/lib/ffidb/exporters/haskell.rb +3 -0
  34. data/lib/ffidb/exporters/java.rb +39 -0
  35. data/lib/ffidb/exporters/json.rb +38 -0
  36. data/lib/ffidb/exporters/julia.rb +3 -0
  37. data/lib/ffidb/exporters/lisp.rb +41 -0
  38. data/lib/ffidb/exporters/luajit.rb +3 -0
  39. data/lib/ffidb/exporters/nim.rb +4 -0
  40. data/lib/ffidb/exporters/nodejs.rb +4 -0
  41. data/lib/ffidb/exporters/ocaml.rb +4 -0
  42. data/lib/ffidb/exporters/php.rb +4 -0
  43. data/lib/ffidb/exporters/python.rb +35 -0
  44. data/lib/ffidb/exporters/racket.rb +3 -0
  45. data/lib/ffidb/exporters/ruby.rb +33 -0
  46. data/lib/ffidb/exporters/rust.rb +5 -0
  47. data/lib/ffidb/exporters/yaml.rb +31 -0
  48. data/lib/ffidb/exporters/zig.rb +3 -0
  49. data/lib/ffidb/function.rb +70 -0
  50. data/lib/ffidb/glob.rb +28 -0
  51. data/lib/ffidb/header.rb +19 -0
  52. data/lib/ffidb/header_parser.rb +339 -0
  53. data/lib/ffidb/library.rb +120 -0
  54. data/lib/ffidb/library_parser.rb +132 -0
  55. data/lib/ffidb/location.rb +17 -0
  56. data/lib/ffidb/parameter.rb +35 -0
  57. data/lib/ffidb/registry.rb +87 -0
  58. data/lib/ffidb/release.rb +14 -0
  59. data/lib/ffidb/struct.rb +41 -0
  60. data/lib/ffidb/symbol_table.rb +90 -0
  61. data/lib/ffidb/symbolic.rb +67 -0
  62. data/lib/ffidb/sysexits.rb +21 -0
  63. data/lib/ffidb/type.rb +214 -0
  64. data/lib/ffidb/typedef.rb +38 -0
  65. data/lib/ffidb/union.rb +37 -0
  66. data/lib/ffidb/version.rb +21 -0
  67. metadata +197 -0
@@ -0,0 +1,49 @@
1
+ <% if header? %>
2
+ ; <%= FFIDB.header %>
3
+
4
+ <% end %>
5
+ (asdf:load-system :cffi)
6
+ <% for library in @libraries %>
7
+ <% if library && @functions[library] %>
8
+
9
+ (cffi:define-foreign-library <%= library&.name || :lib %>
10
+ (t (:default "<%= dlopen_paths_for(library).first %>")))
11
+
12
+ (cffi:use-foreign-library <%= library&.name || :lib %>)
13
+ <% end %>
14
+ <% for enum in @enums[library] || [] %>
15
+
16
+ <% if enum.comment %>
17
+ ;; <%= enum.comment %>
18
+ <% end %>
19
+ (cffi:defcenum <%= enum.name %>
20
+ <% for name, value in enum.values || {} %>
21
+ (:<%= name %> <%= value %>)
22
+ <% end %>
23
+ )
24
+ <% end %>
25
+ <% for struct in @structs[library] || [] %>
26
+
27
+ <% if struct.comment %>
28
+ ;; <%= struct.comment %>
29
+ <% end %>
30
+ <% if struct.opaque? %>
31
+ (cffi:defctype <%= struct.name %> :pointer)
32
+ <% else %>
33
+ (cffi:defcstruct <%= struct.name %>
34
+ <% for name, type in struct.fields || {} %>
35
+ (<%= name %> <%= struct_type(type).map(&:inspect).join(' ') %>)
36
+ <% end %>
37
+ )
38
+ <% end %>
39
+ <% end %>
40
+ <% for function in @functions[library] || [] %>
41
+
42
+ <% if function.comment %>
43
+ ;; <%= function.comment %>
44
+ <% end %>
45
+ (cffi:defcfun "<%= function.name %>" <%= param_type(function.type).inspect %> <%=
46
+ function.parameters.each_value.map { |p| "(#{p.name} #{param_type(p.type).inspect})" }.join(' ')
47
+ %>)
48
+ <% end %>
49
+ <% end %>
@@ -0,0 +1,59 @@
1
+ <% if header? %>
2
+ # <%= FFIDB.header %>
3
+
4
+ <% end %>
5
+ import ctypes, ctypes.util
6
+ <% for library in @libraries %>
7
+ <% if library && @functions[library] %>
8
+
9
+ <%= library&.name || :lib %> = ctypes.CDLL(
10
+ <% self.dlopen_paths_for(library).each_with_index do |library_path, i| %>
11
+ <%= i.nonzero? ? 'or ' : '' %>ctypes.util.find_library("<%= library_path %>")
12
+ <% end %>
13
+ <% if !options[:library_path] && library&.dlopen %>
14
+ or "<%= library.dlopen.first %>"
15
+ <% end %>
16
+ )
17
+ <% end %>
18
+ <% for enum in @enums[library] || [] %>
19
+
20
+ <% if enum.comment %>
21
+ # <%= enum.comment %>
22
+ <% end %>
23
+ <%= enum.name %> = ctypes.c_int
24
+ <% for name, value in enum.values || {} %>
25
+ <%= name %> = <%= value %>
26
+ <% end %>
27
+ <% end %>
28
+ <% for struct in @structs[library] || [] %>
29
+
30
+ <% if struct.comment %>
31
+ # <%= struct.comment %>
32
+ <% end %>
33
+ <% if struct.opaque? %>
34
+ <%= struct.name %> = ctypes.POINTER(ctypes.c_void)
35
+ <% else %>
36
+ class <%= struct.name %>(Structure):
37
+ <% if struct.fields.nil? || struct.fields.empty? %>
38
+ _fields_ = []
39
+ <% else %>
40
+ _fields_ = [
41
+ <% for name, type in struct.fields || {} %>
42
+ (<%= name.to_s.inspect %>, <%= struct_type(type) %>), # <%= type %>
43
+ <% end %>
44
+ ]
45
+ <% end %>
46
+ <% end %>
47
+ <% end %>
48
+ <% for function in @functions[library] || [] %>
49
+
50
+ <% if function.comment %>
51
+ # <%= function.comment %>
52
+ <% end %>
53
+ <%= function.name %> = <%= library&.name || :lib %>.<%= function.name %>
54
+ <%= function.name %>.restype = <%= param_type(function.type) %>
55
+ <%= function.name %>.argtypes = [<%=
56
+ function.parameters.each_value.map { |p| param_type(p.type) }.join(', ')
57
+ %>]
58
+ <% end %>
59
+ <% end %>
@@ -0,0 +1,48 @@
1
+ <% if header? %>
2
+ # <%= FFIDB.header %>
3
+
4
+ <% end %>
5
+ require 'ffi'
6
+ <% for library in @libraries %>
7
+
8
+ module <%= options[:module] || library&.name&.capitalize || :FFI %>
9
+ <% if library && @functions[library] %>
10
+ extend FFI::Library
11
+ ffi_lib [<%= dlopen_paths_for(library).map(&:inspect).join(', ') %>]
12
+ <% end %>
13
+ <% for enum in @enums[library] || [] %>
14
+
15
+ <% if enum.comment %>
16
+ # <%= enum.comment %>
17
+ <% end %>
18
+ <%= enum.name %> = :int
19
+ <% for name, value in enum.values || {} %>
20
+ <%= name %> = <%= value %>
21
+ <% end %>
22
+ <% end %>
23
+ <% for struct in @structs[library] || [] %>
24
+
25
+ <% if struct.comment %>
26
+ # <%= struct.comment %>
27
+ <% end %>
28
+ <% if struct.opaque? %>
29
+ <%= struct.name %> = FFI::Pointer
30
+ <% else %>
31
+ class <%= struct.name %> < FFI::Struct
32
+ <% for (name, type), i in (struct.fields || {}).each_with_index %>
33
+ <%= i.zero? ? 'layout' : ' '*6 %> :<%= name %>, <%= struct_type(type).inspect %><%= (i == (struct.fields || {}).size-1) ? '' : ',' %> # <%= type %>
34
+ <% end %>
35
+ end
36
+ <% end %>
37
+ <% end %>
38
+ <% for function in @functions[library] || [] %>
39
+
40
+ <% if function.comment %>
41
+ # <%= function.comment %>
42
+ <% end %>
43
+ attach_function :<%= function.name %>, [<%=
44
+ function.parameters.each_value.map { |p| param_type(p.type).inspect }.join(', ')
45
+ %>], :<%= param_type(function.type) %>
46
+ <% end %>
47
+ end # <%= options[:module] || library&.name&.capitalize || :FFI %>
48
+ <% end %>
@@ -0,0 +1,34 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require_relative 'ffidb/enum'
4
+ require_relative 'ffidb/errors'
5
+ require_relative 'ffidb/exporter'
6
+ require_relative 'ffidb/exporters'
7
+ require_relative 'ffidb/function'
8
+ require_relative 'ffidb/glob'
9
+ require_relative 'ffidb/header'
10
+ require_relative 'ffidb/header_parser'
11
+ require_relative 'ffidb/library'
12
+ require_relative 'ffidb/library_parser'
13
+ require_relative 'ffidb/location'
14
+ require_relative 'ffidb/parameter'
15
+ require_relative 'ffidb/registry'
16
+ require_relative 'ffidb/release'
17
+ require_relative 'ffidb/struct'
18
+ require_relative 'ffidb/symbolic'
19
+ require_relative 'ffidb/symbol_table'
20
+ require_relative 'ffidb/sysexits'
21
+ require_relative 'ffidb/type'
22
+ require_relative 'ffidb/typedef'
23
+ require_relative 'ffidb/union'
24
+ require_relative 'ffidb/version'
25
+
26
+ module FFIDB
27
+ HEADER = "This is free and unencumbered software released into the public domain.".freeze
28
+
29
+ ##
30
+ # @return [String]
31
+ def self.header
32
+ HEADER
33
+ end
34
+ end # FFIDB
@@ -0,0 +1,37 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require_relative 'symbolic'
4
+
5
+ module FFIDB
6
+ class Enum < ::Struct.new(:name, :values, :comment)
7
+ include Symbolic
8
+
9
+ ##
10
+ # @param [Symbol, #to_sym] name
11
+ # @param [Map<String, Integer>] values
12
+ # @param [String, #to_s] comment
13
+ def initialize(name, values = {}, comment = nil)
14
+ super(name.to_sym, values || {}, comment&.to_s)
15
+ end
16
+
17
+ ##
18
+ # @return [Boolean]
19
+ def enum?() return true end
20
+
21
+ ##
22
+ # @return [String]
23
+ def to_s
24
+ "enum #{self.name}"
25
+ end
26
+
27
+ ##
28
+ # @return [Hash<Symbol, Type>]
29
+ def to_h
30
+ {
31
+ name: self.name.to_s,
32
+ comment: self.comment,
33
+ values: self.values,
34
+ }.delete_if { |k, v| v.nil? }
35
+ end
36
+ end # Enum
37
+ end # FFIDB
@@ -0,0 +1,64 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ require_relative 'sysexits'
4
+
5
+ module FFIDB
6
+ ##
7
+ # Base class for FFIDB errors.
8
+ class Error < StandardError
9
+ EXIT_CODE = Sysexits::EX_SOFTWARE
10
+
11
+ ##
12
+ # @return [Integer]
13
+ def exit_code
14
+ self.class.const_get(:EXIT_CODE)
15
+ end
16
+ end
17
+
18
+ ##
19
+ # An error indicating that a problem with the registry.
20
+ class RegistryError < Error
21
+ EXIT_CODE = Sysexits::EX_CONFIG
22
+ end
23
+
24
+ ##
25
+ # An error indicating that opening a registry directory requires a newer
26
+ # version of FFIDB.rb than the current one.
27
+ class RegistryVersionMismatch < RegistryError; end
28
+
29
+ ##
30
+ # An error indicating that an FFI symbol was not found.
31
+ class SymbolNotFound < Error
32
+ EXIT_CODE = Sysexits::EX_NOINPUT
33
+
34
+ def initialize(symbol_kind, symbol_name)
35
+ super("#{symbol_kind.to_s.capitalize} not found: #{symbol_name}")
36
+ end
37
+ end
38
+
39
+ ##
40
+ # An error indicating that an FFI function was not found.
41
+ class FunctionNotFound < SymbolNotFound
42
+ def initialize(function_name)
43
+ super(:function, function_name)
44
+ end
45
+ end
46
+
47
+ ##
48
+ # A warning raised during header file parsing.
49
+ class ParseWarning < Error
50
+ EXIT_CODE = Sysexits::EX_DATAERR
51
+ end
52
+
53
+ ##
54
+ # An error raised during header file parsing.
55
+ class ParseError < Error
56
+ EXIT_CODE = Sysexits::EX_DATAERR
57
+ end
58
+
59
+ ##
60
+ # A fatal error raised during header file parsing.
61
+ class ParsePanic < Error
62
+ EXIT_CODE = Sysexits::EX_DATAERR
63
+ end
64
+ end # FFIDB
@@ -0,0 +1,141 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ #require 'erb'
4
+ require 'tilt' # https://rubygems.org/gems/tilt
5
+ require 'yaml'
6
+
7
+ module FFIDB
8
+ class Exporter
9
+ def self.for(format) # TODO
10
+ require_relative 'exporters'
11
+ case format&.to_sym
12
+ when :c, :c99, :c11, :c18 then Exporters::C
13
+ when :'c++', :'c++11', :'c++14', :'c++17', :'c++20', :cpp, :cxx then Exporters::Cpp
14
+ when :csv then Exporters::CSV
15
+ when :dart, :flutter then Exporters::Dart
16
+ when :go, :cgo then Exporters::Go
17
+ when :java, :jna then Exporters::Java
18
+ when :json then Exporters::JSON
19
+ when :lisp, :'common-lisp' then Exporters::Lisp
20
+ when :python, :py then Exporters::Python
21
+ when :ruby, :rb then Exporters::Ruby
22
+ # TODO: csharp, haskell, julia, luajit, nim, nodejs, ocaml, php, racket, rust, zig
23
+ when :yaml then Exporters::YAML
24
+ else raise "unknown output format: #{format}"
25
+ end
26
+ end
27
+
28
+ attr_reader :options
29
+
30
+ def initialize(stream = $stdout, **kwargs)
31
+ @stream = stream
32
+ @options = kwargs.transform_keys(&:to_sym).freeze
33
+ end
34
+
35
+ def debug?
36
+ self.options[:debug]
37
+ end
38
+
39
+ def verbose?
40
+ self.options[:verbose] || self.debug?
41
+ end
42
+
43
+ def header?
44
+ self.options[:header]
45
+ end
46
+
47
+ def emit(&block)
48
+ begin
49
+ self.begin
50
+ yield self
51
+ self.finish
52
+ ensure
53
+ self.close
54
+ end
55
+ end
56
+
57
+ def dlopen_paths_for(library)
58
+ if library_path = self.options[:library_path]
59
+ library.objects.map { |lib| library_path.delete_suffix('/') << "/" << lib }
60
+ else
61
+ library.objects + library.dlopen
62
+ end
63
+ end
64
+
65
+ def begin() end
66
+
67
+ def begin_library(library)
68
+ @library = library
69
+ @libraries ||= []
70
+ @libraries << library
71
+ @typedefs ||= {}
72
+ @enums ||= {}
73
+ @structs ||= {}
74
+ @unions ||= {}
75
+ @functions ||= {}
76
+ end
77
+
78
+ def export_header(header)
79
+ header.typedefs.sort.each { |typedef| self.export_typedef(typedef) }
80
+ header.enums.sort.each { |enum| self.export_enum(enum) }
81
+ header.structs.sort.each { |struct| self.export_struct(struct) }
82
+ header.unions.sort.each { |union| self.export_union(union) }
83
+ header.functions.sort.each { |function| self.export_function(function) }
84
+ end
85
+
86
+ def export_symbol(symbol, disabled: nil)
87
+ self.__send__("export_#{symbol.kind}", symbol, disabled: disabled)
88
+ end
89
+
90
+ def export_typedef(typedef, disabled: nil)
91
+ (@typedefs[@library] ||= []) << typedef
92
+ end
93
+
94
+ def export_enum(enum, disabled: nil)
95
+ (@enums[@library] ||= []) << enum
96
+ end
97
+
98
+ def export_struct(struct, disabled: nil)
99
+ (@structs[@library] ||= []) << struct
100
+ end
101
+
102
+ def export_union(union, disabled: nil)
103
+ (@unions[@library] ||= []) << union
104
+ end
105
+
106
+ def export_function(function, disabled: nil)
107
+ (@functions[@library] ||= []) << function
108
+ end
109
+
110
+ def finish_library
111
+ @library = nil
112
+ end
113
+
114
+ def finish() end
115
+
116
+ def close() end
117
+
118
+ protected
119
+
120
+ def puts(*args)
121
+ @stream.puts *args
122
+ end
123
+
124
+ def print(*args)
125
+ @stream.print *args
126
+ end
127
+
128
+ def render_template(template_name)
129
+ #ERB.new(self.load_template(template_name)).result(binding)
130
+ Tilt.new(self.path_to_template(template_name)).render(self)
131
+ end
132
+
133
+ def load_template(template_name)
134
+ File.read(self.path_to_template(template_name))
135
+ end
136
+
137
+ def path_to_template(template_name)
138
+ File.expand_path("../../etc/templates/#{template_name}", __dir__)
139
+ end
140
+ end # Exporter
141
+ end # FFIDB
@@ -0,0 +1,28 @@
1
+ # This is free and unencumbered software released into the public domain.
2
+
3
+ module FFIDB
4
+ module Exporters; end
5
+ end
6
+
7
+ require_relative 'exporters/c'
8
+ require_relative 'exporters/cpp'
9
+ require_relative 'exporters/csharp'
10
+ require_relative 'exporters/csv'
11
+ require_relative 'exporters/dart'
12
+ require_relative 'exporters/go'
13
+ require_relative 'exporters/haskell'
14
+ require_relative 'exporters/java'
15
+ require_relative 'exporters/julia'
16
+ require_relative 'exporters/json'
17
+ require_relative 'exporters/lisp'
18
+ require_relative 'exporters/luajit'
19
+ require_relative 'exporters/nim'
20
+ require_relative 'exporters/nodejs'
21
+ require_relative 'exporters/ocaml'
22
+ require_relative 'exporters/php'
23
+ require_relative 'exporters/python'
24
+ require_relative 'exporters/racket'
25
+ require_relative 'exporters/ruby'
26
+ require_relative 'exporters/rust'
27
+ require_relative 'exporters/yaml'
28
+ require_relative 'exporters/zig'