ffi-compiler2 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 31b9ab69f716cfa4ccdbbfd43453cd4620763b74
4
+ data.tar.gz: 92bb862b4bf5b7283461dfdb66891809785a3fd9
5
+ SHA512:
6
+ metadata.gz: 21c4caf0223071245552777b21300797699c5158fe0473bb7bd55171f023ec312141a45faec0c706d1dc90aecf627afcf1f6df37a6e93de9e732f75d59bc9961
7
+ data.tar.gz: f8ab05891126e75451ed2b7e500a413f0d3bd767523bf586b310c3c73dcbc3602fea6ca034dad3c28f4e79cabd465ad6d2802a8daf2de12364bab311a7153c97
data/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+
2
+ [ffi-compiler](https://github.com/ffi/ffi-compiler) is a ruby library for automating compilation of native libraries for use with [ffi](https://github.com/ffi/ffi)
3
+
4
+ To use, define your own ruby->native API using ffi, implement it in C, then use ffi-compiler to compile it.
5
+
6
+ Example
7
+ ------
8
+
9
+ ###### Directory layout
10
+ lib
11
+ |- example
12
+ |- example.rb
13
+
14
+ ext
15
+ |- example.c
16
+ |- Rakefile
17
+
18
+ example.gemspec
19
+
20
+ ###### lib/example/example.rb
21
+ require 'ffi'
22
+ require 'ffi-compiler/loader'
23
+
24
+ module Example
25
+ extend FFI::Library
26
+ ffi_lib FFI::Compiler::Loader.find('example')
27
+
28
+ # example function which takes no parameters and returns long
29
+ attach_function :example, [], :long
30
+ end
31
+
32
+ ###### ext/example.c
33
+ long
34
+ example(void)
35
+ {
36
+ return 0xdeadbeef;
37
+ }
38
+
39
+ ###### ext/Rakefile
40
+ require 'ffi-compiler/compile_task'
41
+
42
+ FFI::Compiler::CompileTask.new('example') do |c|
43
+ c.have_header?('stdio.h', '/usr/local/include')
44
+ c.have_func?('puts')
45
+ c.have_library?('z')
46
+ end
47
+
48
+ ###### example.gemspec
49
+ Gem::Specification.new do |s|
50
+ s.extensions << 'ext/Rakefile'
51
+ s.name = 'example'
52
+ s.version = '0.0.1'
53
+ s.email = 'ffi-example'
54
+ s.files = %w(example.gemspec) + Dir.glob("{lib,spec,ext}/**/*")
55
+ s.add_dependency 'rake'
56
+ s.add_dependency 'ffi-compiler'
57
+ end
58
+
59
+ ###### Build gem and install it
60
+ gem build example.gemspec && gem install example-0.0.1.gem
61
+ Successfully built RubyGem
62
+ Name: example
63
+ Version: 0.0.1
64
+ File: example-0.0.1.gem
65
+ Building native extensions. This could take a while...
66
+ Successfully installed example-0.0.1
67
+
68
+ ###### Test it
69
+ $ irb
70
+ 2.0.0dev :001 > require 'example/example'
71
+ => true
72
+ 2.0.0dev :002 > puts "Example.example=#{Example.example.to_s(16)}"
73
+ Example.example=deadbeef
74
+ => nil
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'rubygems/tasks'
3
+
4
+ Gem::Tasks.new do |t|
5
+ t.scm.tag.format = '%s'
6
+ end
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'ffi-compiler2'
3
+ s.version = '2.0.0'
4
+ s.author = 'Dāvis'
5
+ s.email = 'davispuh@gmail.com'
6
+ s.homepage = 'https://gitlab.com/davispuh/ffi-compiler'
7
+ s.summary = 'Ruby FFI Rakefile generator'
8
+ s.description = 'Ruby FFI library'
9
+ s.files = %w(ffi-compiler.gemspec README.md Rakefile LICENSE) + Dir.glob("{lib,spec}/**/*")
10
+ s.has_rdoc = false
11
+ s.license = 'Apache-2.0'
12
+ s.required_ruby_version = '>= 1.9'
13
+ s.add_dependency 'rake'
14
+ s.add_dependency 'ffi', '>= 1.0.0'
15
+ s.add_development_dependency 'rspec'
16
+ s.add_development_dependency 'rubygems-tasks'
17
+ end
18
+
@@ -0,0 +1,243 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'rake/clean'
4
+ require 'ffi'
5
+ require 'tmpdir'
6
+ require 'rbconfig'
7
+ require_relative 'platform'
8
+
9
+ module FFI
10
+ module Compiler
11
+ DEFAULT_CFLAGS = %w(-fexceptions -O -fno-omit-frame-pointer -fno-strict-aliasing)
12
+ DEFAULT_LDFLAGS = %w(-fexceptions)
13
+
14
+ class CompileTask < Rake::TaskLib
15
+ attr_reader :cflags, :cxxflags, :ldflags, :libs, :platform
16
+ attr_accessor :name, :ext_dir, :source_dirs, :exclude
17
+
18
+ def initialize(name)
19
+ @name = File.basename(name)
20
+ @ext_dir = File.dirname(name)
21
+ @source_dirs = [@ext_dir]
22
+ @exclude = []
23
+ @defines = []
24
+ @include_paths = []
25
+ @library_paths = []
26
+ @libraries = []
27
+ @headers = []
28
+ @functions = []
29
+ @cflags = DEFAULT_CFLAGS.dup
30
+ @cxxflags = DEFAULT_CFLAGS.dup
31
+ @ldflags = DEFAULT_LDFLAGS.dup
32
+ @libs = []
33
+ @platform = Platform.system
34
+ @exports = []
35
+
36
+ yield self if block_given?
37
+ define_task!
38
+ end
39
+
40
+ def add_include_path(path)
41
+ @include_paths << path
42
+ end
43
+
44
+ def add_define(name, value=1)
45
+ @defines << "-D#{name}=#{value}"
46
+ end
47
+
48
+ def have_func?(func)
49
+ main = <<-C_FILE
50
+ extern void #{func}();
51
+ int main(int argc, char **argv) { #{func}(); return 0; }
52
+ C_FILE
53
+
54
+ if try_compile(main)
55
+ @functions << func
56
+ return true
57
+ end
58
+ false
59
+ end
60
+
61
+ def have_header?(header, *paths)
62
+ try_header(header, @include_paths) || try_header(header, paths)
63
+ end
64
+
65
+ def have_library?(libname, *paths)
66
+ try_library(libname, paths: @library_paths) || try_library(libname, paths: paths)
67
+ end
68
+
69
+ def have_library(lib, func = nil, headers = nil, &b)
70
+ try_library(lib, function: func, headers: headers, paths: @library_paths)
71
+ end
72
+
73
+ def find_library(lib, func, *paths)
74
+ try_library(lib, function: func, paths: @library_paths) || try_library(libname, function: func, paths: paths)
75
+ end
76
+
77
+ def export(rb_file)
78
+ @exports << { :rb_file => rb_file, :header => File.join(@ext_dir, File.basename(rb_file).sub(/\.rb$/, '.h')) }
79
+ end
80
+
81
+ private
82
+ def define_task!
83
+ pic_flags = %w(-fPIC)
84
+ so_flags = []
85
+
86
+ if @platform.mac?
87
+ pic_flags = []
88
+ so_flags << '-bundle'
89
+
90
+ elsif @platform.name =~ /linux/
91
+ so_flags << "-shared -Wl,-soname,#{lib_name}"
92
+
93
+ else
94
+ so_flags << '-shared'
95
+ end
96
+ so_flags = so_flags.join(' ')
97
+
98
+ out_dir = "#{@platform.arch}-#{@platform.os}"
99
+ if @ext_dir != '.'
100
+ out_dir = File.join(@ext_dir, out_dir)
101
+ end
102
+
103
+ directory(out_dir)
104
+ CLOBBER.include(out_dir)
105
+
106
+ lib_name = File.join(out_dir, Platform.system.map_library_name(@name))
107
+
108
+ iflags = @include_paths.uniq.map { |p| "-I#{p}" }
109
+ @defines << @functions.uniq.map { |f| "-DHAVE_#{f.upcase}=1" }
110
+ @defines << @headers.uniq.map { |h| "-DHAVE_#{h.upcase.sub(/\./, '_')}=1" }
111
+
112
+ cflags = (@cflags + pic_flags + iflags + @defines).join(' ')
113
+ cxxflags = (@cxxflags + @cflags + pic_flags + iflags + @defines).join(' ')
114
+ ld_flags = (@library_paths.map { |path| "-L#{path}" } + @ldflags).join(' ')
115
+ libs = (@libraries.map { |l| "-l#{l}" } + @libs).join(' ')
116
+
117
+ src_files = []
118
+ obj_files = []
119
+ @source_dirs.each do |dir|
120
+ files = FileList["#{dir}/**/*.{c,cpp}"]
121
+ unless @exclude.empty?
122
+ files.delete_if { |f| f =~ Regexp.union(*@exclude) }
123
+ end
124
+ src_files += files
125
+ obj_files += files.ext('.o').map { |f| File.join(out_dir, f.sub(/^#{dir}\//, '')) }
126
+ end
127
+
128
+ index = 0
129
+ src_files.each do |src|
130
+ obj_file = obj_files[index]
131
+ if src =~ /\.c$/
132
+ file obj_file => [ src, File.dirname(obj_file) ] do |t|
133
+ sh "#{cc} #{cflags} -o #{t.name} -c #{t.prerequisites[0]}"
134
+ end
135
+
136
+ else
137
+ file obj_file => [ src, File.dirname(obj_file) ] do |t|
138
+ sh "#{cxx} #{cxxflags} -o #{t.name} -c #{t.prerequisites[0]}"
139
+ end
140
+ end
141
+
142
+ CLEAN.include(obj_file)
143
+ index += 1
144
+ end
145
+
146
+ ld = src_files.detect { |f| f =~ /\.cpp$/ } ? cxx : cc
147
+
148
+ # create all the directories for the output files
149
+ obj_files.map { |f| File.dirname(f) }.sort.uniq.map { |d| directory d }
150
+
151
+ desc "Build dynamic library"
152
+ file lib_name => obj_files do |t|
153
+ sh "#{ld} #{so_flags} -o #{t.name} #{t.prerequisites.join(' ')} #{ld_flags} #{libs}"
154
+ end
155
+ CLEAN.include(lib_name)
156
+
157
+ @exports.each do |e|
158
+ desc "Export #{e[:rb_file]}"
159
+ file e[:header] => [ e[:rb_file] ] do |t|
160
+ ruby "-I#{File.join(File.dirname(__FILE__), 'fake_ffi')} -I#{File.dirname(t.prerequisites[0])} #{File.join(File.dirname(__FILE__), 'exporter.rb')} #{t.prerequisites[0]} #{t.name}"
161
+ end
162
+
163
+ obj_files.each { |o| file o => [ e[:header] ] }
164
+ CLEAN.include(e[:header])
165
+
166
+ desc "Export API headers"
167
+ task :api_headers => [ e[:header] ]
168
+ end
169
+
170
+ task :default => [ lib_name ]
171
+ task :package => [ :api_headers ]
172
+ end
173
+
174
+ def try_header(header, paths)
175
+ main = <<-C_FILE
176
+ #include <#{header}>
177
+ int main(int argc, char **argv) { return 0; }
178
+ C_FILE
179
+
180
+ if paths.empty? && try_compile(main)
181
+ @headers << header
182
+ return true
183
+ end
184
+
185
+ paths.each do |path|
186
+ if try_compile(main, "-I#{path}")
187
+ @include_paths << path
188
+ @headers << header
189
+ return true
190
+ end
191
+ end
192
+ false
193
+ end
194
+
195
+
196
+ def try_library(libname, options = {})
197
+ func = options[:function] || 'main'
198
+ paths = options[:paths] || ''
199
+ main = <<-C_FILE
200
+ #{(options[:headers] || []).map {|h| "#include <#{h}>"}.join('\n')}
201
+ extern int #{func}();
202
+ int main() { return #{func}(); }
203
+ C_FILE
204
+
205
+ if paths.empty? && try_compile(main)
206
+ @libraries << libname
207
+ return true
208
+ end
209
+
210
+ paths.each do |path|
211
+ if try_compile(main, "-L#{path}", "-l#{libname}")
212
+ @library_paths << path
213
+ @libraries << libname
214
+ end
215
+ end
216
+ end
217
+
218
+ def try_compile(src, *opts)
219
+ Dir.mktmpdir do |dir|
220
+ path = File.join(dir, 'ffi-test.c')
221
+ File.open(path, 'w') do |f|
222
+ f << src
223
+ end
224
+ cflags = opts.join(' ')
225
+ output = File.join(dir, 'ffi-test')
226
+ begin
227
+ return system "#{cc} #{cflags} -o #{output} -c #{path} > #{path}.log 2>&1"
228
+ rescue
229
+ return false
230
+ end
231
+ end
232
+ end
233
+
234
+ def cc
235
+ @cc ||= (ENV['CC'] || RbConfig::CONFIG['CC'] || 'cc')
236
+ end
237
+
238
+ def cxx
239
+ @cxx ||= (ENV['CXX'] || RbConfig::CONFIG['CXX'] || 'c++')
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,49 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'rake/clean'
4
+
5
+ module FFI
6
+ module Compiler
7
+ class ExportTask < Rake::TaskLib
8
+
9
+ def initialize(rb_dir, out_dir, options = {})
10
+ @rb_dir = rb_dir
11
+ @out_dir = out_dir
12
+ @gem_spec = options[:gem_spec]
13
+ @exports = []
14
+
15
+ if block_given?
16
+ yield self
17
+ define_tasks!
18
+ end
19
+ end
20
+
21
+ def export(rb_file)
22
+ @exports << { :rb_file => File.join(@rb_dir, rb_file), :header => File.join(@out_dir, File.basename(rb_file).sub(/\.rb$/, '.h')) }
23
+ end
24
+
25
+ def export_all
26
+ Dir["#@rb_dir/**/*rb"].each do |rb_file|
27
+ @exports << { :rb_file => rb_file, :header => File.join(@out_dir, File.basename(rb_file).sub(/\.rb$/, '.h')) }
28
+ end
29
+ end
30
+
31
+ private
32
+ def define_tasks!
33
+ @exports.each do |e|
34
+ file e[:header] => [ e[:rb_file] ] do |t|
35
+ ruby "-I#{File.join(File.dirname(__FILE__), 'fake_ffi')} #{File.join(File.dirname(__FILE__), 'exporter.rb')} #{t.prerequisites[0]} #{t.name}"
36
+ end
37
+ CLEAN.include(e[:header])
38
+
39
+ desc "Export API headers"
40
+ task :api_headers => [ e[:header] ]
41
+ @gem_spec.files << e[:header] unless @gem_spec.nil?
42
+ end
43
+
44
+ task :gem => [ :api_headers ] unless @gem_spec.nil?
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'ffi'
2
+
3
+ load ARGV[0]
4
+ FFI.exporter.dump(ARGV[1])
@@ -0,0 +1,8 @@
1
+ module FFI
2
+ module Compiler
3
+ module Loader
4
+ def self.find(*args)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,282 @@
1
+ module FFI
2
+
3
+ def self.exporter=(exporter)
4
+ @@exporter = exporter
5
+ end
6
+
7
+ def self.exporter
8
+ @@exporter ||= Exporter.new(nil)
9
+ end
10
+
11
+ class Type
12
+ attr_reader :name
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+ end
17
+
18
+ class StructByReference < Type
19
+ def initialize(struct_class)
20
+ super("struct #{struct_class.to_s.gsub('::', '_')} *")
21
+ end
22
+ end
23
+
24
+ class StructByValue < Type
25
+ def initialize(struct_class)
26
+ super("struct #{struct_class.to_s.gsub('::', '_')}")
27
+ end
28
+ end
29
+
30
+ class CallbackInfo
31
+ attr_reader :return_type
32
+ attr_reader :arg_types
33
+ attr_reader :options
34
+
35
+ def initialize(return_type, arg_types = [], *other)
36
+ @return_type = return_type
37
+ @arg_types = arg_types
38
+ @options = options
39
+ end
40
+
41
+ def name(name)
42
+ params = @arg_types.empty? ? 'void' : @arg_types.map(&:name).join(', ')
43
+ "#{@return_type.name} (*#{name})(#{params})"
44
+ end
45
+ end
46
+
47
+ PrimitiveTypes = {
48
+ :void => 'void',
49
+ :bool => 'bool',
50
+ :string => 'const char *',
51
+ :char => 'char',
52
+ :uchar => 'unsigned char',
53
+ :short => 'short',
54
+ :ushort => 'unsigned short',
55
+ :int => 'int',
56
+ :uint => 'unsigned int',
57
+ :long => 'long',
58
+ :ulong => 'unsigned long',
59
+ :long_long => 'long long',
60
+ :ulong_long => 'unsigned long long',
61
+ :float => 'float',
62
+ :double => 'double',
63
+ :long_double => 'long double',
64
+ :pointer => 'void *',
65
+ :int8 => 'int8_t',
66
+ :uint8 => 'uint8_t',
67
+ :int16 => 'int16_t',
68
+ :uint16 => 'uint16_t',
69
+ :int32 => 'int32_t',
70
+ :uint32 => 'uint32_t',
71
+ :int64 => 'int64_t',
72
+ :uint64 => 'uint64_t',
73
+ :buffer_in => 'const in void *',
74
+ :buffer_out => 'out void *',
75
+ :buffer_inout => 'inout void *',
76
+ :varargs => '...'
77
+ }
78
+
79
+ TypeMap = {}
80
+ def self.find_type(type)
81
+ return type if type.is_a?(Type) or type.is_a?(CallbackInfo)
82
+
83
+ t = TypeMap[type]
84
+ return t unless t.nil?
85
+
86
+ if PrimitiveTypes.has_key?(type)
87
+ return TypeMap[type] = Type.new(PrimitiveTypes[type])
88
+ end
89
+ raise TypeError.new("cannot resolve type #{type}")
90
+ end
91
+
92
+ class Function
93
+ def initialize(*args)
94
+ end
95
+ end
96
+
97
+ class Exporter
98
+ attr_accessor :mod
99
+ attr_reader :functions, :callbacks, :structs
100
+
101
+ def initialize(mod)
102
+ @mod = mod
103
+ @functions = []
104
+ @callbacks = {}
105
+ @structs = []
106
+ end
107
+
108
+ def attach(mname, fname, result_type, param_types)
109
+ @functions << { mname: mname, fname: fname, result_type: result_type, params: param_types.dup }
110
+ end
111
+
112
+ def struct(name, fields)
113
+ @structs << { name: name, fields: fields.dup }
114
+ end
115
+
116
+ def callback(name, cb)
117
+ @callbacks[name] = cb
118
+ end
119
+
120
+ def dump(out_file)
121
+ File.open(out_file, 'w') do |f|
122
+ guard = File.basename(out_file).upcase.gsub('.', '_').gsub('/', '_')
123
+ f.puts <<-HEADER
124
+ #ifndef #{guard}
125
+ #define #{guard} 1
126
+
127
+ #ifndef RBFFI_EXPORT
128
+ # ifdef __cplusplus
129
+ # define RBFFI_EXPORT extern "C"
130
+ # else
131
+ # define RBFFI_EXPORT
132
+ # endif
133
+ #endif
134
+
135
+ HEADER
136
+
137
+ @callbacks.each do |name, cb|
138
+ f.puts "typedef #{cb.name(name)};"
139
+ end
140
+ @structs.each do |s|
141
+ f.puts "struct #{s[:name].gsub('::', '_')} {"
142
+ s[:fields].each do |field|
143
+ if field[:type].is_a?(CallbackInfo)
144
+ type = field[:type].name(field[:name].to_s)
145
+ else
146
+ type = "#{field[:type].name} #{field[:name].to_s}"
147
+ end
148
+ f.puts "#{' ' * 4}#{type};"
149
+ end
150
+ f.puts '};'
151
+ f.puts
152
+ end
153
+ @functions.each do |fn|
154
+ param_string = fn[:params].empty? ? 'void' : fn[:params].map(&:name).join(', ')
155
+ f.puts "RBFFI_EXPORT #{fn[:result_type].name} #{fn[:fname]}(#{param_string});"
156
+ end
157
+ f.puts <<-EPILOG
158
+
159
+ #endif /* #{guard} */
160
+ EPILOG
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ module Library
167
+ def self.extended(mod)
168
+ FFI.exporter.mod = mod
169
+ end
170
+
171
+ def attach_function(name, func, args, returns = nil, options = nil)
172
+ mname, a2, a3, a4, a5 = name, func, args, returns, options
173
+ cname, arg_types, ret_type, opts = (a4 && (a2.is_a?(String) || a2.is_a?(Symbol))) ? [ a2, a3, a4, a5 ] : [ mname.to_s, a2, a3, a4 ]
174
+ arg_types = arg_types.map { |e| find_type(e) }
175
+ FFI.exporter.attach(mname, cname, find_type(ret_type), arg_types)
176
+ end
177
+
178
+ def ffi_lib(*args)
179
+
180
+ end
181
+
182
+ def callback(*args)
183
+ name, params, ret = if args.length == 3
184
+ args
185
+ else
186
+ [ nil, args[0], args[1] ]
187
+ end
188
+ native_params = params.map { |e| find_type(e) }
189
+ cb = FFI::CallbackInfo.new(find_type(ret), native_params)
190
+ FFI.exporter.callback(name, cb) if name
191
+ end
192
+
193
+ TypeMap = {}
194
+ def find_type(type)
195
+ t = TypeMap[type]
196
+ return t unless t.nil?
197
+
198
+ if type.is_a?(Class) && type < Struct
199
+ return TypeMap[type] = StructByReference.new(type)
200
+ end
201
+
202
+ TypeMap[type] = FFI.find_type(type)
203
+ end
204
+ end
205
+
206
+ class Struct
207
+ def self.layout(*args)
208
+ return if args.size.zero?
209
+ fields = []
210
+ if args.first.kind_of?(Hash)
211
+ args.first.each do |name, type|
212
+ fields << { :name => name, :type => find_type(type), :offset => nil }
213
+ end
214
+ else
215
+ i = 0
216
+ while i < args.size
217
+ name, type, offset = args[i], args[i+1], nil
218
+ i += 2
219
+ if args[i].kind_of?(Integer)
220
+ offset = args[i]
221
+ i += 1
222
+ end
223
+ fields << { :name => name, :type => find_type(type), :offset => offset }
224
+ end
225
+ end
226
+ FFI.exporter.struct(self.to_s, fields)
227
+ end
228
+
229
+ def initialize
230
+ @data = {}
231
+ end
232
+
233
+ def [](name)
234
+ @data[name]
235
+ end
236
+
237
+ def []=(name, value)
238
+ @data[name] = value
239
+ end
240
+
241
+ def self.callback(params, ret)
242
+ FFI::CallbackInfo.new(find_type(ret), params.map { |e| find_type(e) })
243
+ end
244
+
245
+ TypeMap = {}
246
+ def self.find_type(type)
247
+ t = TypeMap[type]
248
+ return t unless t.nil?
249
+
250
+ if type.is_a?(Class) && type < Struct
251
+ return TypeMap[type] = StructByValue.new(type)
252
+ end
253
+
254
+ TypeMap[type] = FFI.find_type(type)
255
+ end
256
+
257
+ def self.in
258
+ ptr(:in)
259
+ end
260
+
261
+ def self.out
262
+ ptr(:out)
263
+ end
264
+
265
+ def self.ptr(flags = :inout)
266
+ StructByReference.new(self)
267
+ end
268
+
269
+ def self.val
270
+ StructByValue.new(self)
271
+ end
272
+
273
+ def self.by_value
274
+ self.val
275
+ end
276
+
277
+ def self.by_ref(flags = :inout)
278
+ self.ptr(flags)
279
+ end
280
+
281
+ end
282
+ end
@@ -0,0 +1,36 @@
1
+ require 'pathname'
2
+ require 'ffi'
3
+ require_relative 'platform'
4
+
5
+ module FFI
6
+ module Compiler
7
+ module Loader
8
+ def self.find(name, start_path = nil)
9
+ library = Platform.system.map_library_name(name)
10
+ root = false
11
+ Pathname.new(start_path || caller_path(caller[0])).ascend do |path|
12
+ Dir.glob("#{path}/**/{#{FFI::Platform::ARCH}-#{FFI::Platform::OS}/#{library},#{library}}") do |f|
13
+ return f
14
+ end
15
+
16
+ break if root
17
+
18
+ # Next iteration will be the root of the gem if this is the lib/ dir - stop after that
19
+ root = File.basename(path) == 'lib'
20
+ end
21
+ raise LoadError.new("cannot find '#{name}' library")
22
+ end
23
+
24
+ def self.caller_path(line = caller[0])
25
+ if FFI::Platform::OS == 'windows'
26
+ drive = line[0..1]
27
+ path = line[2..-1].split(/:/)[0]
28
+ full_path = drive + path
29
+ else
30
+ full_path = line.split(/:/)[0]
31
+ end
32
+ File.dirname full_path
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ module FFI::Compiler
2
+ class Platform
3
+ LIBSUFFIX = FFI::Platform.mac? ? 'bundle' : FFI::Platform::LIBSUFFIX
4
+
5
+ def self.system
6
+ @@system ||= Platform.new
7
+ end
8
+
9
+ def map_library_name(name)
10
+ "#{FFI::Platform::LIBPREFIX}#{name}.#{LIBSUFFIX}"
11
+ end
12
+
13
+ def arch
14
+ FFI::Platform::ARCH
15
+ end
16
+
17
+ def os
18
+ FFI::Platform::OS
19
+ end
20
+
21
+ def name
22
+ FFI::Platform.name
23
+ end
24
+
25
+ def mac?
26
+ FFI::Platform.mac?
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+ require 'rake/clean'
4
+ require 'ffi'
5
+ require 'tmpdir'
6
+ require 'rbconfig'
7
+ require_relative 'compile_task'
8
+
9
+ module FFI
10
+ module Compiler
11
+ class Task < CompileTask
12
+ warn "#{self} is deprecated"
13
+ end
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffi-compiler2
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Dāvis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ffi
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubygems-tasks
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Ruby FFI library
70
+ email: davispuh@gmail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - LICENSE
76
+ - README.md
77
+ - Rakefile
78
+ - ffi-compiler.gemspec
79
+ - lib/ffi-compiler/compile_task.rb
80
+ - lib/ffi-compiler/export_task.rb
81
+ - lib/ffi-compiler/exporter.rb
82
+ - lib/ffi-compiler/fake_ffi/ffi-compiler/loader.rb
83
+ - lib/ffi-compiler/fake_ffi/ffi.rb
84
+ - lib/ffi-compiler/loader.rb
85
+ - lib/ffi-compiler/platform.rb
86
+ - lib/ffi-compiler/task.rb
87
+ homepage: https://gitlab.com/davispuh/ffi-compiler
88
+ licenses:
89
+ - Apache-2.0
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '1.9'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.6.4
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Ruby FFI Rakefile generator
111
+ test_files: []