ffidb 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/CHANGES.md +7 -0
- data/CREDITS.md +2 -0
- data/README.md +201 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/bin/ffidb +387 -0
- data/etc/mappings/dart.yaml +35 -0
- data/etc/mappings/java.yaml +36 -0
- data/etc/mappings/lisp.yaml +35 -0
- data/etc/mappings/python.yaml +35 -0
- data/etc/mappings/ruby.yaml +35 -0
- data/etc/templates/c.erb +46 -0
- data/etc/templates/cpp.erb +45 -0
- data/etc/templates/dart.erb +64 -0
- data/etc/templates/go.erb +50 -0
- data/etc/templates/java.erb +56 -0
- data/etc/templates/lisp.erb +49 -0
- data/etc/templates/python.erb +59 -0
- data/etc/templates/ruby.erb +48 -0
- data/lib/ffidb.rb +34 -0
- data/lib/ffidb/enum.rb +37 -0
- data/lib/ffidb/errors.rb +64 -0
- data/lib/ffidb/exporter.rb +141 -0
- data/lib/ffidb/exporters.rb +28 -0
- data/lib/ffidb/exporters/c.rb +52 -0
- data/lib/ffidb/exporters/cpp.rb +13 -0
- data/lib/ffidb/exporters/csharp.rb +6 -0
- data/lib/ffidb/exporters/csv.rb +24 -0
- data/lib/ffidb/exporters/dart.rb +60 -0
- data/lib/ffidb/exporters/go.rb +16 -0
- data/lib/ffidb/exporters/haskell.rb +3 -0
- data/lib/ffidb/exporters/java.rb +39 -0
- data/lib/ffidb/exporters/json.rb +38 -0
- data/lib/ffidb/exporters/julia.rb +3 -0
- data/lib/ffidb/exporters/lisp.rb +41 -0
- data/lib/ffidb/exporters/luajit.rb +3 -0
- data/lib/ffidb/exporters/nim.rb +4 -0
- data/lib/ffidb/exporters/nodejs.rb +4 -0
- data/lib/ffidb/exporters/ocaml.rb +4 -0
- data/lib/ffidb/exporters/php.rb +4 -0
- data/lib/ffidb/exporters/python.rb +35 -0
- data/lib/ffidb/exporters/racket.rb +3 -0
- data/lib/ffidb/exporters/ruby.rb +33 -0
- data/lib/ffidb/exporters/rust.rb +5 -0
- data/lib/ffidb/exporters/yaml.rb +31 -0
- data/lib/ffidb/exporters/zig.rb +3 -0
- data/lib/ffidb/function.rb +70 -0
- data/lib/ffidb/glob.rb +28 -0
- data/lib/ffidb/header.rb +19 -0
- data/lib/ffidb/header_parser.rb +339 -0
- data/lib/ffidb/library.rb +120 -0
- data/lib/ffidb/library_parser.rb +132 -0
- data/lib/ffidb/location.rb +17 -0
- data/lib/ffidb/parameter.rb +35 -0
- data/lib/ffidb/registry.rb +87 -0
- data/lib/ffidb/release.rb +14 -0
- data/lib/ffidb/struct.rb +41 -0
- data/lib/ffidb/symbol_table.rb +90 -0
- data/lib/ffidb/symbolic.rb +67 -0
- data/lib/ffidb/sysexits.rb +21 -0
- data/lib/ffidb/type.rb +214 -0
- data/lib/ffidb/typedef.rb +38 -0
- data/lib/ffidb/union.rb +37 -0
- data/lib/ffidb/version.rb +21 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 832a289161ba25e274a8a6d3a20a1592ec40bff4e3774400e6d7f5b3286335c5
|
4
|
+
data.tar.gz: 6b27cb31d52109e2a44c5c8a72f830b772d306206acec33add96eb8faf23e464
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3b6bc06df8f1d94ded39f0370874d6c02c2f6dc33321f8a7affc51a3f53e2db5300b9291447b5528758a9ba5b22e3402a45b01f5ee17cca5acb642db531298ea
|
7
|
+
data.tar.gz: 4fba31365dbb2b0567dd381075b8f36c3d3bcf07324c9edf143f19e7472e7eabe1c6a17a00fff7b945e55ca47db33b81d1c7eb034e28de32753bff0adb1da820
|
data/AUTHORS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* Arto Bendiken <arto@bendiken.net>
|
data/CHANGES.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Changelog
|
2
|
+
=========
|
3
|
+
|
4
|
+
All notable changes to this project will be documented in this file.
|
5
|
+
|
6
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
7
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
data/CREDITS.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
FFI DB Command-Line Interface (CLI)
|
2
|
+
===================================
|
3
|
+
|
4
|
+
[](https://unlicense.org)
|
5
|
+
[](https://rubygems.org/gems/ffidb)
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
The tool can be installed quickly and easily on any computer that has
|
11
|
+
[Ruby](https://www.ruby-lang.org/en/) available:
|
12
|
+
|
13
|
+
$ gem install ffidb
|
14
|
+
|
15
|
+
After installation, download and initialize the FFI DB registry as follows:
|
16
|
+
|
17
|
+
$ ffidb init
|
18
|
+
|
19
|
+
Your local FFI DB registry is located at the path `$HOME/.ffidb/`.
|
20
|
+
|
21
|
+
Examples (API)
|
22
|
+
--------------
|
23
|
+
|
24
|
+
### Loading the library
|
25
|
+
|
26
|
+
require 'ffidb'
|
27
|
+
|
28
|
+
### Enumerating FFI functions
|
29
|
+
|
30
|
+
FFIDB::Registry.open do |registry|
|
31
|
+
registry.open_library(:zlib) do |library|
|
32
|
+
library.each_function do |function|
|
33
|
+
p function
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Reference (CLI)
|
39
|
+
---------------
|
40
|
+
|
41
|
+
Commands:
|
42
|
+
ffidb export LIBRARY|SYMBOL... # Generate C/C++/Go/Java/Python/Ruby/etc code
|
43
|
+
ffidb help [COMMAND] # Describe available commands or one specific command
|
44
|
+
ffidb init # Initialize the registry (at: ~/.ffidb)
|
45
|
+
ffidb list [LIBRARY] # List FFI libraries and symbols
|
46
|
+
ffidb parse HEADER... # Parse .h header files
|
47
|
+
ffidb search PATTERN # Search for FFI symbols using a glob pattern
|
48
|
+
ffidb show SYMBOL # Show FFI symbol information
|
49
|
+
ffidb update # Fetch updates to the registry (at: ~/.ffidb)
|
50
|
+
|
51
|
+
Options:
|
52
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
53
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
54
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
55
|
+
|
56
|
+
### Initializing the Registry
|
57
|
+
|
58
|
+
Usage:
|
59
|
+
ffidb init
|
60
|
+
|
61
|
+
Options:
|
62
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
63
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
64
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
65
|
+
|
66
|
+
Description:
|
67
|
+
Initializes the local FFIDB registry, a prerequisite for using FFIDB.
|
68
|
+
|
69
|
+
Your local FFIDB registry is located at $HOME/.ffidb.
|
70
|
+
|
71
|
+
This command is equivalent to:
|
72
|
+
|
73
|
+
$ git clone --depth=1 https://github.com/ffidb/ffidb.git $HOME/.ffidb
|
74
|
+
|
75
|
+
### Updating the Registry
|
76
|
+
|
77
|
+
Usage:
|
78
|
+
ffidb update
|
79
|
+
|
80
|
+
Options:
|
81
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
82
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
83
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
84
|
+
|
85
|
+
Description:
|
86
|
+
Updates the local FFIDB registry, pulling updates from GitHub.
|
87
|
+
|
88
|
+
Your local FFIDB registry is located at $HOME/.ffidb.
|
89
|
+
|
90
|
+
This command is equivalent to:
|
91
|
+
|
92
|
+
$ cd $HOME/.ffidb && git pull
|
93
|
+
|
94
|
+
### Listing Libraries and Symbols
|
95
|
+
|
96
|
+
Usage:
|
97
|
+
ffidb list [LIBRARY]
|
98
|
+
|
99
|
+
Options:
|
100
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
101
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
102
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
103
|
+
|
104
|
+
Description:
|
105
|
+
Lists libraries with their FFI symbols (such as functions).
|
106
|
+
|
107
|
+
For example:
|
108
|
+
|
109
|
+
$ ffidb list lua
|
110
|
+
|
111
|
+
### Searching Libraries and Symbols
|
112
|
+
|
113
|
+
Usage:
|
114
|
+
ffidb search PATTERN
|
115
|
+
|
116
|
+
Options:
|
117
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
118
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
119
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
120
|
+
|
121
|
+
Description:
|
122
|
+
Searches for FFI symbols (for example, functions) using a glob pattern.
|
123
|
+
|
124
|
+
For example:
|
125
|
+
|
126
|
+
$ ffidb search sqlite3_*_blob
|
127
|
+
|
128
|
+
### Viewing Symbol Information
|
129
|
+
|
130
|
+
Usage:
|
131
|
+
ffidb show SYMBOL
|
132
|
+
|
133
|
+
Options:
|
134
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
135
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
136
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
137
|
+
|
138
|
+
Description:
|
139
|
+
Shows information about an FFI symbol (for example, a function).
|
140
|
+
|
141
|
+
For example:
|
142
|
+
|
143
|
+
$ ffidb show lua_callk
|
144
|
+
|
145
|
+
### Generating FFI Bindings
|
146
|
+
|
147
|
+
Usage:
|
148
|
+
ffidb export LIBRARY|SYMBOL...
|
149
|
+
|
150
|
+
Options:
|
151
|
+
-f, [--format=FORMAT] # Specify the output FORMAT (for example: java)
|
152
|
+
-L, [--library-path=DIRECTORY] # Load all libraries from DIRECTORY
|
153
|
+
[--exclude=PATTERN] # Exclude symbols matching the glob PATTERN
|
154
|
+
[--exclude-from=FILE] # Read exclude patterns from FILE
|
155
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
156
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
157
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
158
|
+
|
159
|
+
Description:
|
160
|
+
Generates code for a target language (e.g., C/C++/Java/Python/Ruby/etc).
|
161
|
+
|
162
|
+
Currently supported target formats and programming languages:
|
163
|
+
|
164
|
+
--format=c # C99
|
165
|
+
--format=c++ # C++11
|
166
|
+
--format=dart # Dart & Flutter
|
167
|
+
--format=go # Go (cgo)
|
168
|
+
--format=java # Java (JNA)
|
169
|
+
--format=json # JSON
|
170
|
+
--format=lisp # Common Lisp (CFFI)
|
171
|
+
--format=python # Python (ctypes)
|
172
|
+
--format=ruby # Ruby (FFI)
|
173
|
+
--format=yaml # YAML
|
174
|
+
|
175
|
+
For example:
|
176
|
+
|
177
|
+
$ ffidb export lua -f=c # Export Lua bindings as C code
|
178
|
+
$ ffidb export lua -f=cpp # Export Lua bindings as C++ code
|
179
|
+
$ ffidb export lua -f=java # Export Lua bindings as Java JNA code
|
180
|
+
$ ffidb export lua -f=python # Export Lua bindings as Python ctypes code
|
181
|
+
$ ffidb export lua -f=ruby # Export Lua bindings as Ruby FFI code
|
182
|
+
|
183
|
+
### Parsing C Header Files
|
184
|
+
|
185
|
+
Usage:
|
186
|
+
ffidb parse HEADER...
|
187
|
+
|
188
|
+
Options:
|
189
|
+
-C, [--config=FILE] # Use a library.yaml configuration FILE
|
190
|
+
-D, [--define=VAR[=VAL]] # Define VAR as a preprocessor symbol
|
191
|
+
-I, [--include=DIRECTORY] # Add DIRECTORY to the headers search path
|
192
|
+
-d, [--debug], [--no-debug] # Enable debugging
|
193
|
+
-v, [--verbose], [--no-verbose] # Be verbose (print warnings)
|
194
|
+
-q, [--quiet], [--no-quiet] # Be quiet (silence non-fatal errors)
|
195
|
+
|
196
|
+
Description:
|
197
|
+
Parses .h header files, outputting YAML using the FFIDB schema.
|
198
|
+
|
199
|
+
Note: parsing requires installation of the 'ffi-clang' library:
|
200
|
+
|
201
|
+
$ gem install ffi-clang
|
data/UNLICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <https://unlicense.org/>
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.12.0
|
data/bin/ffidb
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
#!/usr/bin/env ruby -W1
|
2
|
+
# This is free and unencumbered software released into the public domain.
|
3
|
+
|
4
|
+
require_relative '../lib/ffidb'
|
5
|
+
|
6
|
+
require 'thor' # https://rubygems.org/gems/thor
|
7
|
+
|
8
|
+
require 'pathname'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
class CLI < Thor
|
12
|
+
include FFIDB::Sysexits
|
13
|
+
|
14
|
+
def self.exit_on_failure?() true end
|
15
|
+
|
16
|
+
class_option :debug, aliases: '-d', type: :boolean, desc: "Enable debugging"
|
17
|
+
class_option :verbose, aliases: '-v', type: :boolean, desc: "Be verbose (print warnings)"
|
18
|
+
class_option :quiet, aliases: '-q', type: :boolean, desc: "Be quiet (silence non-fatal errors)"
|
19
|
+
|
20
|
+
desc "export LIBRARY|SYMBOL...", "Generate C/C++/Go/Java/Python/Ruby/etc code"
|
21
|
+
long_desc <<~EOS
|
22
|
+
Generates code for a target language (e.g., C/C++/Java/Python/Ruby/etc).
|
23
|
+
|
24
|
+
Currently supported target formats and programming languages:
|
25
|
+
|
26
|
+
--format=c # C99
|
27
|
+
--format=c++ # C++11
|
28
|
+
--format=dart # Dart & Flutter
|
29
|
+
--format=go # Go (cgo)
|
30
|
+
--format=java # Java (JNA)
|
31
|
+
--format=json # JSON
|
32
|
+
--format=lisp # Common Lisp (CFFI)
|
33
|
+
--format=python # Python (ctypes)
|
34
|
+
--format=ruby # Ruby (FFI)
|
35
|
+
--format=yaml # YAML
|
36
|
+
|
37
|
+
For example:
|
38
|
+
|
39
|
+
$ ffidb export lua -f=c # Export Lua bindings as C code
|
40
|
+
$ ffidb export lua -f=cpp # Export Lua bindings as C++ code
|
41
|
+
$ ffidb export lua -f=java # Export Lua bindings as Java JNA code
|
42
|
+
$ ffidb export lua -f=python # Export Lua bindings as Python ctypes code
|
43
|
+
$ ffidb export lua -f=ruby # Export Lua bindings as Ruby FFI code
|
44
|
+
EOS
|
45
|
+
option :format, aliases: '-f', default: nil, desc: "Specify the output FORMAT (for example: java)"
|
46
|
+
option :type, aliases: '-t', default: nil, desc: "Specify the symbol TYPE (enum/struct/function)"
|
47
|
+
option :header, type: :boolean, default: true, desc: "Output the file header, or not"
|
48
|
+
option :module, aliases: '-M', default: nil, desc: "Provide name for the top-level module or namespace"
|
49
|
+
option :library_path, aliases: '-L', banner: 'DIRECTORY', desc: "Load all libraries from DIRECTORY"
|
50
|
+
option :exclude, repeatable: true, banner: 'PATTERN', desc: "Exclude symbols matching the glob PATTERN"
|
51
|
+
option :exclude_from, repeatable: true, banner: 'FILE', desc: "Read exclude patterns from FILE"
|
52
|
+
option :disable, repeatable: true, banner: 'PATTERN', desc: "Disable symbols matching the glob PATTERN"
|
53
|
+
option :disable_from, repeatable: true, banner: 'FILE', desc: "Read disable patterns from FILE"
|
54
|
+
def export(pattern, *patterns)
|
55
|
+
patterns.prepend(pattern)
|
56
|
+
symbol_kind = self.options[:type] ? self.options[:type].to_sym : nil
|
57
|
+
|
58
|
+
format = (self.options[:format] || 'yaml').to_sym
|
59
|
+
|
60
|
+
excludes = []
|
61
|
+
(self.options[:exclude] || []).each do |symbol|
|
62
|
+
excludes += symbol.include?(',') ? symbol.split(',') : [symbol]
|
63
|
+
end
|
64
|
+
(self.options[:exclude_from] || []).map do |exclude_path|
|
65
|
+
exclude_path = Pathname(exclude_path)
|
66
|
+
raise Errno::ENOENT, exclude_path.to_s unless exclude_path.exist?
|
67
|
+
exclude_path.each_line { |line| excludes << line.chomp }
|
68
|
+
end
|
69
|
+
excludes.sort!.uniq!
|
70
|
+
excludes.map! do |exclude_pattern|
|
71
|
+
FFIDB::Glob.new(exclude_pattern, ignore_case: false, match_substring: false)
|
72
|
+
end
|
73
|
+
|
74
|
+
disables = []
|
75
|
+
(self.options[:disable] || []).each do |symbol|
|
76
|
+
disables += symbol.include?(',') ? symbol.split(',') : [symbol]
|
77
|
+
end
|
78
|
+
(self.options[:disable_from] || []).map do |disable_path|
|
79
|
+
disable_path = Pathname(disable_path)
|
80
|
+
raise Errno::ENOENT, disable_path.to_s unless disable_path.exist?
|
81
|
+
disable_path.each_line { |line| disables << line.chomp }
|
82
|
+
end
|
83
|
+
disables.sort!.uniq!
|
84
|
+
disables.map! do |disable_pattern|
|
85
|
+
FFIDB::Glob.new(disable_pattern, ignore_case: false, match_substring: false)
|
86
|
+
end
|
87
|
+
|
88
|
+
self.check_registry_exists!
|
89
|
+
FFIDB::Registry.open do |registry|
|
90
|
+
exports = []
|
91
|
+
patterns.each do |pattern|
|
92
|
+
if library = registry.open_library(pattern)
|
93
|
+
library.each_symbol do |symbol|
|
94
|
+
next if symbol_kind && symbol_kind != symbol.kind
|
95
|
+
exports << [library, symbol.kind_weight, symbol] unless excludes.any? { |x| x === symbol.name }
|
96
|
+
end
|
97
|
+
else
|
98
|
+
matcher = FFIDB::Glob.new(pattern, ignore_case: true, match_substring: false)
|
99
|
+
registry.find_symbols(matcher) do |symbol, library|
|
100
|
+
next if symbol_kind && symbol_kind != symbol.kind
|
101
|
+
exports << [library, symbol.kind_weight, symbol] unless excludes.any? { |x| x === symbol.name }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
FFIDB::Exporter.for(format).new(**options).emit do |export|
|
107
|
+
prev_library = nil
|
108
|
+
exports.sort!.uniq.each do |library, _, symbol|
|
109
|
+
if library != prev_library
|
110
|
+
export.finish_library if prev_library
|
111
|
+
export.begin_library(library)
|
112
|
+
prev_library = library
|
113
|
+
end
|
114
|
+
disabled = disables.any? { |pattern| pattern === symbol.name }
|
115
|
+
export.export_symbol(symbol, disabled: disabled)
|
116
|
+
end
|
117
|
+
export.finish_library if prev_library
|
118
|
+
end
|
119
|
+
end
|
120
|
+
rescue => error
|
121
|
+
raise error if debug?
|
122
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
123
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
124
|
+
end
|
125
|
+
|
126
|
+
desc "init", "Initialize the registry (at: ~/.ffidb)"
|
127
|
+
long_desc <<~EOS
|
128
|
+
Initializes the local FFIDB registry, a prerequisite for using FFIDB.
|
129
|
+
|
130
|
+
Your local FFIDB registry is located at $HOME/.ffidb (#{FFIDB::Registry.default_path}).
|
131
|
+
|
132
|
+
This command is equivalent to:
|
133
|
+
|
134
|
+
$ git clone --depth=1 #{FFIDB::Registry::GIT_HTTPS_URL} $HOME/.ffidb
|
135
|
+
EOS
|
136
|
+
def init
|
137
|
+
registry_path = FFIDB::Registry.default_path
|
138
|
+
if registry_path.exist?
|
139
|
+
error = "The registry at #{registry_path} has already been initialized."
|
140
|
+
warn "#{$0}: #{set_color(error, :yellow)}" if verbose?
|
141
|
+
return
|
142
|
+
end
|
143
|
+
git_command = %Q(git clone --depth=1 #{FFIDB::Registry::GIT_HTTPS_URL} #{registry_path})
|
144
|
+
system git_command # TODO: improve this
|
145
|
+
if $?.exitstatus.nonzero?
|
146
|
+
error = "Failed to execute `#{git_command}`: exit code #{$?.exitstatus}."
|
147
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
148
|
+
exit $?.exitstatus
|
149
|
+
end
|
150
|
+
Dir.chdir registry_path
|
151
|
+
rescue => error
|
152
|
+
raise error if debug?
|
153
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
154
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
155
|
+
end
|
156
|
+
|
157
|
+
desc "list [LIBRARY]", "List FFI libraries and symbols"
|
158
|
+
long_desc <<~EOS
|
159
|
+
Lists libraries with their FFI symbols (such as functions).
|
160
|
+
|
161
|
+
For example:
|
162
|
+
|
163
|
+
$ ffidb list lua
|
164
|
+
EOS
|
165
|
+
option :type, aliases: '-t', default: nil, desc: "Specify the symbol TYPE (enum/struct/function)"
|
166
|
+
def list(library_name = nil)
|
167
|
+
symbol_kind = self.options[:type] ? self.options[:type].to_sym : nil
|
168
|
+
self.check_registry_exists!
|
169
|
+
FFIDB::Registry.open do |registry|
|
170
|
+
registry.each_library do |library|
|
171
|
+
next if library_name && library_name != library.name
|
172
|
+
p library if debug?
|
173
|
+
library.each_symbol do |symbol|
|
174
|
+
next if symbol_kind && symbol_kind != symbol.kind
|
175
|
+
p symbol if debug?
|
176
|
+
print "#{library.name}"
|
177
|
+
print verbose? ? "@#{library.version}" : '' # TODO: resolve stable symlinks
|
178
|
+
print "\t#{symbol.name}"
|
179
|
+
if verbose?
|
180
|
+
print "\t#{symbol.kind}"
|
181
|
+
print "\t\t// #{symbol.definition.to_s}" if symbol.function?
|
182
|
+
end
|
183
|
+
puts
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
rescue => error
|
188
|
+
raise error if debug?
|
189
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
190
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
191
|
+
end
|
192
|
+
|
193
|
+
desc "parse HEADER...", "Parse .h header files"
|
194
|
+
long_desc <<~EOS
|
195
|
+
Parses .h header files, outputting YAML using the FFIDB schema.
|
196
|
+
|
197
|
+
Note: parsing requires installation of the 'ffi-clang' library:
|
198
|
+
|
199
|
+
$ gem install ffi-clang
|
200
|
+
EOS
|
201
|
+
option :config, aliases: '-C', banner: 'FILE', desc: "Use a library.yaml configuration FILE"
|
202
|
+
option :define, aliases: '-D', repeatable: true, banner: 'VAR[=VAL]', desc: "Define VAR as a preprocessor symbol"
|
203
|
+
option :include, aliases: '-I', repeatable: true, banner: 'DIRECTORY', desc: "Add DIRECTORY to the headers search path"
|
204
|
+
option :format, aliases: '-f', default: nil, desc: "Specify the output FORMAT (default: yaml)"
|
205
|
+
def parse(path, *paths)
|
206
|
+
base_directory = nil
|
207
|
+
paths.prepend(path)
|
208
|
+
paths = paths.inject([]) do |paths, path|
|
209
|
+
path = Pathname(path)
|
210
|
+
case
|
211
|
+
when !path.exist?
|
212
|
+
raise "Path does not exist: #{path}"
|
213
|
+
when path.directory?
|
214
|
+
base_directory = path if base_directory.nil?
|
215
|
+
paths.concat(Dir["#{path}/**/*.h"].sort.map { |p| Pathname(p) })
|
216
|
+
else paths << path
|
217
|
+
end
|
218
|
+
end
|
219
|
+
base_directory = paths.first.dirname if base_directory.nil?
|
220
|
+
|
221
|
+
begin
|
222
|
+
$VERBOSE = nil # suppress deprecation warnings from ffi-clang
|
223
|
+
require 'ffi/clang' # https://rubygems.org/gems/ffi-clang
|
224
|
+
$VERBOSE = false
|
225
|
+
rescue LoadError => error
|
226
|
+
raise error if debug?
|
227
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
228
|
+
exit EX_UNAVAILABLE
|
229
|
+
end
|
230
|
+
|
231
|
+
FFIDB::HeaderParser.new(base_directory: base_directory, debug: verbose? || debug?).tap do |parser|
|
232
|
+
# Parse a library.yaml configuration file, if given:
|
233
|
+
library = nil
|
234
|
+
if config_path = self.options[:config]
|
235
|
+
config_path = Pathname(config_path)
|
236
|
+
raise Errno::ENOENT, config_path.to_s unless config_path.exist?
|
237
|
+
config = YAML.load(config_path.read).transform_keys(&:to_sym)
|
238
|
+
(config[:configure] || []).each do |var_and_val|
|
239
|
+
parser.parse_macro! var_and_val
|
240
|
+
end
|
241
|
+
(config[:exclude] || []).each do |symbol|
|
242
|
+
parser.exclude_symbols[symbol] = true
|
243
|
+
end
|
244
|
+
(config[:include] || []).each do |symbol|
|
245
|
+
parser.include_symbols[symbol] = true
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Parse and define all specified -D preprocessor symbols:
|
250
|
+
(self.options[:define] || []).each do |var_and_val|
|
251
|
+
parser.parse_macro! var_and_val
|
252
|
+
end
|
253
|
+
|
254
|
+
# Add all specified -I directories to the headers search path:
|
255
|
+
(self.options[:include] || []).each do |dir_path|
|
256
|
+
parser.add_include_path! Pathname(dir_path).expand_path
|
257
|
+
end
|
258
|
+
|
259
|
+
FFIDB::Exporter.for(self.options[:format] || :yaml).new(**options).emit do |export|
|
260
|
+
export.begin_library(library)
|
261
|
+
paths.each do |path|
|
262
|
+
header = parser.parse_header(path) do |exception|
|
263
|
+
case exception
|
264
|
+
when FFIDB::ParseError
|
265
|
+
warn "#{$0}: #{set_color(exception.to_s, :red)}" unless quiet?
|
266
|
+
when FFIDB::ParseWarning
|
267
|
+
warn "#{$0}: #{set_color(exception.to_s, :yellow)}" if verbose?
|
268
|
+
else raise exception
|
269
|
+
end
|
270
|
+
end
|
271
|
+
export.export_header(header)
|
272
|
+
end
|
273
|
+
export.finish_library
|
274
|
+
end
|
275
|
+
end
|
276
|
+
rescue => error
|
277
|
+
raise error if debug?
|
278
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
279
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
280
|
+
end
|
281
|
+
|
282
|
+
desc "search PATTERN", "Search for FFI symbols using a glob pattern"
|
283
|
+
long_desc <<~EOS
|
284
|
+
Searches for FFI symbols (for example, functions) using a glob pattern.
|
285
|
+
|
286
|
+
For example:
|
287
|
+
|
288
|
+
$ ffidb search sqlite3_*_blob
|
289
|
+
EOS
|
290
|
+
option :type, aliases: '-t', default: nil, desc: "Specify the symbol TYPE (enum/struct/function)"
|
291
|
+
def search(pattern)
|
292
|
+
symbol_kind = self.options[:type] ? self.options[:type].to_sym : nil
|
293
|
+
matcher = FFIDB::Glob.new(pattern, ignore_case: true, match_substring: true)
|
294
|
+
p self.options, matcher if debug?
|
295
|
+
self.check_registry_exists!
|
296
|
+
FFIDB::Registry.open do |registry|
|
297
|
+
registry.find_symbols(matcher, kind: symbol_kind) do |symbol, library|
|
298
|
+
puts "#{library.name}\t#{symbol.name}" # TODO: improve formatting
|
299
|
+
end
|
300
|
+
end
|
301
|
+
rescue => error
|
302
|
+
raise error if debug?
|
303
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
304
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
305
|
+
end
|
306
|
+
|
307
|
+
desc "show SYMBOL", "Show FFI symbol information"
|
308
|
+
long_desc <<~EOS
|
309
|
+
Shows information about an FFI symbol (for example, a function).
|
310
|
+
|
311
|
+
For example:
|
312
|
+
|
313
|
+
$ ffidb show lua_callk
|
314
|
+
EOS
|
315
|
+
option :type, aliases: '-t', default: nil, desc: "Specify the symbol TYPE (enum/struct/function)"
|
316
|
+
def show(symbol_name)
|
317
|
+
symbol_kind = self.options[:type] ? self.options[:type].to_sym : nil
|
318
|
+
p self.options, symbol_name if debug?
|
319
|
+
self.check_registry_exists!
|
320
|
+
FFIDB::Registry.open do |registry|
|
321
|
+
found = registry.find_symbols(symbol_name, kind: symbol_kind) do |symbol, library|
|
322
|
+
puts symbol.to_yaml
|
323
|
+
puts
|
324
|
+
end
|
325
|
+
raise FFIDB::SymbolNotFound.new(symbol_kind || :symbol, symbol_name) unless found
|
326
|
+
end
|
327
|
+
rescue => error
|
328
|
+
raise error if debug?
|
329
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
330
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
331
|
+
end
|
332
|
+
|
333
|
+
desc "update", "Fetch updates to the registry (at: ~/.ffidb)"
|
334
|
+
long_desc <<~EOS
|
335
|
+
Updates the local FFIDB registry, pulling updates from GitHub.
|
336
|
+
|
337
|
+
Your local FFIDB registry is located at $HOME/.ffidb (#{FFIDB::Registry.default_path}).
|
338
|
+
|
339
|
+
This command is equivalent to:
|
340
|
+
|
341
|
+
$ cd $HOME/.ffidb && git pull
|
342
|
+
EOS
|
343
|
+
def update
|
344
|
+
registry_path = self.check_registry_exists!
|
345
|
+
Dir.chdir registry_path
|
346
|
+
git_command = %Q(git pull)
|
347
|
+
system git_command # TODO: improve this
|
348
|
+
if $?.exitstatus.nonzero?
|
349
|
+
error = "Failed to execute `#{git_command}` in #{registry_path}: exit code #{$?.exitstatus}."
|
350
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
351
|
+
exit $?.exitstatus
|
352
|
+
end
|
353
|
+
rescue => error
|
354
|
+
raise error if debug?
|
355
|
+
warn "#{$0}: #{set_color(error, :red)}"
|
356
|
+
exit error.respond_to?(:exit_code) ? error.exit_code : EX_SOFTWARE
|
357
|
+
end
|
358
|
+
|
359
|
+
protected
|
360
|
+
|
361
|
+
def check_registry_exists!
|
362
|
+
registry_path = FFIDB::Registry.default_path
|
363
|
+
raise FFIDB::RegistryError, "Registry at #{registry_path} not initialized (run `#{$0} init` first?)" unless registry_path.exist?
|
364
|
+
registry_path
|
365
|
+
end
|
366
|
+
|
367
|
+
def debug?() self.options[:debug] end
|
368
|
+
def verbose?() self.options[:verbose] || self.debug? end
|
369
|
+
def quiet?() self.options[:quiet] end
|
370
|
+
end # CLI
|
371
|
+
|
372
|
+
# Fix for https://github.com/erikhuda/thor/issues/398
|
373
|
+
class Thor::Shell::Basic
|
374
|
+
def print_wrapped(message, options = {})
|
375
|
+
indent = (options[:indent] || 0).to_i
|
376
|
+
if indent.zero?
|
377
|
+
self.stdout.puts message
|
378
|
+
else
|
379
|
+
message.each_line do |message_line|
|
380
|
+
self.stdout.print ' ' * indent
|
381
|
+
self.stdout.puts message_line.chomp
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end # Thor::Shell::Basic
|
386
|
+
|
387
|
+
CLI.start(ARGV)
|