ffidb 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Project license](https://img.shields.io/badge/license-Public%20Domain-blue.svg)](https://unlicense.org)
|
5
|
+
[![RubyGems gem version](https://img.shields.io/gem/v/ffidb.svg)](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)
|