ffi-inline 0.0.3 → 0.0.4
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.
- data/examples/ex_1.rb +2 -2
- data/ffi-inline.gemspec +2 -2
- data/lib/ffi/inline.rb +18 -0
- data/lib/ffi/inline/builders.rb +108 -0
- data/lib/ffi/inline/builders/c.rb +155 -0
- data/lib/ffi/inline/builders/cpp.rb +38 -0
- data/lib/ffi/inline/compilers.rb +63 -0
- data/lib/ffi/inline/compilers/gcc.rb +67 -0
- data/lib/ffi/inline/compilers/gxx.rb +33 -0
- data/lib/ffi/inline/compilers/tcc.rb +67 -0
- data/lib/ffi/inline/error.rb +21 -0
- data/lib/ffi/inline/inline.rb +83 -0
- data/lib/ffi/inline/version.rb +17 -0
- metadata +13 -13
- data/lib/ffi/inliner.rb +0 -8
- data/lib/ffi/inliner/builders.rb +0 -98
- data/lib/ffi/inliner/builders/c.rb +0 -147
- data/lib/ffi/inliner/builders/cpp.rb +0 -28
- data/lib/ffi/inliner/compilers.rb +0 -53
- data/lib/ffi/inliner/compilers/gcc.rb +0 -57
- data/lib/ffi/inliner/compilers/gxx.rb +0 -23
- data/lib/ffi/inliner/compilers/tcc.rb +0 -57
- data/lib/ffi/inliner/error.rb +0 -11
- data/lib/ffi/inliner/inliner.rb +0 -73
- data/lib/ffi/inliner/version.rb +0 -7
data/examples/ex_1.rb
CHANGED
data/ffi-inline.gemspec
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
Kernel.load 'lib/ffi/
|
1
|
+
Kernel.load 'lib/ffi/inline/version.rb'
|
2
2
|
|
3
3
|
Gem::Specification.new {|s|
|
4
4
|
s.name = 'ffi-inline'
|
5
|
-
s.version = FFI::
|
5
|
+
s.version = FFI::Inline::VERSION
|
6
6
|
s.authors = 'meh.'
|
7
7
|
s.email = 'meh@paranoici.org'
|
8
8
|
s.homepage = 'http://github.com/meh/ruby-ffi-inline'
|
data/lib/ffi/inline.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'digest/sha1'
|
12
|
+
require 'fileutils'
|
13
|
+
require 'rbconfig'
|
14
|
+
require 'shellwords'
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
require 'ffi'
|
18
|
+
require 'ffi/inline/inline'
|
@@ -0,0 +1,108 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module FFI; module Inline
|
12
|
+
|
13
|
+
Signature = ::Struct.new(:return, :name, :arguments, :arity, :blocking)
|
14
|
+
|
15
|
+
class Builder
|
16
|
+
@builders = []
|
17
|
+
|
18
|
+
def self.[] (name)
|
19
|
+
return name if name.is_a?(Builder)
|
20
|
+
|
21
|
+
@builders.find {|builder|
|
22
|
+
builder.name.downcase == name.downcase ||
|
23
|
+
builder.aliases.any? { |ali| ali.downcase == name.downcase }
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.define (name, *aliases, &block)
|
28
|
+
inherit_from = self
|
29
|
+
|
30
|
+
if name.is_a?(Builder)
|
31
|
+
name = name.class
|
32
|
+
end
|
33
|
+
|
34
|
+
if name.is_a?(Class)
|
35
|
+
inherit_from = name
|
36
|
+
name = aliases.shift
|
37
|
+
end
|
38
|
+
|
39
|
+
@builders << Class.new(inherit_from, &block).tap {|k|
|
40
|
+
k.instance_eval {
|
41
|
+
define_singleton_method :name do name end
|
42
|
+
define_singleton_method :aliases do aliases end
|
43
|
+
}
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :code, :compiler
|
48
|
+
|
49
|
+
def initialize (code = '')
|
50
|
+
@code = code
|
51
|
+
@evals = []
|
52
|
+
end
|
53
|
+
|
54
|
+
def use_compiler (compiler)
|
55
|
+
@compiler = Compiler[compiler]
|
56
|
+
end
|
57
|
+
|
58
|
+
def raw (code)
|
59
|
+
@code << code
|
60
|
+
end
|
61
|
+
|
62
|
+
def eval (&block)
|
63
|
+
@evals << block
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_ffi_type (type)
|
67
|
+
raise 'the Builder has not been specialized'
|
68
|
+
end
|
69
|
+
|
70
|
+
def shared_object
|
71
|
+
raise 'the Builder has not been specialized'
|
72
|
+
end
|
73
|
+
|
74
|
+
def signatures
|
75
|
+
raise 'the Builder has not been specialized'
|
76
|
+
end
|
77
|
+
|
78
|
+
def symbols
|
79
|
+
signatures.map { |s| s.name.to_sym }
|
80
|
+
end
|
81
|
+
|
82
|
+
def build
|
83
|
+
builder = self
|
84
|
+
blocks = @evals
|
85
|
+
|
86
|
+
mod = Module.new
|
87
|
+
mod.instance_eval {
|
88
|
+
extend FFI::Library
|
89
|
+
|
90
|
+
ffi_lib builder.shared_object
|
91
|
+
|
92
|
+
blocks.each { |block| instance_eval &block }
|
93
|
+
|
94
|
+
builder.signatures.each {|s|
|
95
|
+
attach_function s.name, s.arguments.compact.map {|a|
|
96
|
+
builder.to_ffi_type(a, self)
|
97
|
+
}, builder.to_ffi_type(s.return, self), :blocking => s.blocking
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
mod
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end; end
|
106
|
+
|
107
|
+
require 'ffi/inline/builders/c'
|
108
|
+
require 'ffi/inline/builders/cpp'
|
@@ -0,0 +1,155 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'ffi/inline/compilers/tcc'
|
12
|
+
require 'ffi/inline/compilers/gcc'
|
13
|
+
|
14
|
+
module FFI; module Inline
|
15
|
+
|
16
|
+
Builder.define :c do
|
17
|
+
ToFFI = {
|
18
|
+
'void' => :void,
|
19
|
+
'char' => :char,
|
20
|
+
'unsigned char' => :uchar,
|
21
|
+
'int' => :int,
|
22
|
+
'unsigned int' => :uint,
|
23
|
+
'long' => :long,
|
24
|
+
'unsigned long' => :ulong,
|
25
|
+
'float' => :float,
|
26
|
+
'double' => :double,
|
27
|
+
}
|
28
|
+
|
29
|
+
attr_reader :code, :compiler, :libraries
|
30
|
+
|
31
|
+
def initialize (code = '', options = {})
|
32
|
+
super(code)
|
33
|
+
|
34
|
+
@types = ToFFI.dup
|
35
|
+
@libraries = options[:libraries] || []
|
36
|
+
@signatures = []
|
37
|
+
|
38
|
+
use_compiler options[:use_compiler] || options[:compiler] || :gcc
|
39
|
+
|
40
|
+
@signatures << parse_signature(code) if code && !code.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def libraries (*libraries)
|
44
|
+
@libraries.concat(libraries)
|
45
|
+
end
|
46
|
+
|
47
|
+
def types (map = nil)
|
48
|
+
map ? @types.merge!(map) : @types
|
49
|
+
end
|
50
|
+
|
51
|
+
alias map types
|
52
|
+
|
53
|
+
def raw (code, no_line = false)
|
54
|
+
return super(code) if no_line
|
55
|
+
|
56
|
+
whole, path, line = caller.find { |line| line !~ /ffi-inline/ }.match(/^(.*?):(\d+):in/).to_a
|
57
|
+
|
58
|
+
super "\n#line #{line.to_i} #{path.inspect}\n" << code
|
59
|
+
end
|
60
|
+
|
61
|
+
alias c_raw raw
|
62
|
+
|
63
|
+
def include (path, options = {})
|
64
|
+
delimiter = (options[:quoted] || options[:local]) ? ['"', '"'] : ['<', '>']
|
65
|
+
|
66
|
+
raw "#include #{delimiter.first}#{path}#{delimiter.last}\n", true
|
67
|
+
end
|
68
|
+
|
69
|
+
def typedef (from, to)
|
70
|
+
raw "typedef #{from} #{to};"
|
71
|
+
end
|
72
|
+
|
73
|
+
def function (code, signature = nil)
|
74
|
+
parsed = parse_signature(code)
|
75
|
+
|
76
|
+
if signature
|
77
|
+
parsed[:arguments] = signature[:arguments] if signature[:arguments]
|
78
|
+
parsed[:return] = signature[:return] if signature[:return]
|
79
|
+
parsed[:blocking] = signature[:blocking] if signature[:blocking]
|
80
|
+
end
|
81
|
+
|
82
|
+
@signatures << parsed
|
83
|
+
|
84
|
+
raw code
|
85
|
+
end; alias c function
|
86
|
+
|
87
|
+
def struct (ffi_struct)
|
88
|
+
raw %{
|
89
|
+
typedef struct {#{
|
90
|
+
ffi_struct.layout.fields.map {|field|
|
91
|
+
"#{field} #{field.name};"
|
92
|
+
}.join("\n")
|
93
|
+
}} #{ffi_struct.class.name}
|
94
|
+
}, true
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_ffi_type (type, mod = nil)
|
98
|
+
raise ArgumentError, 'type is nil' if type.nil?
|
99
|
+
|
100
|
+
if type.is_a?(Symbol) || type.is_a?(FFI::Type) || (type.is_a?(Class) && type.ancestors.include?(FFI::Struct))
|
101
|
+
type
|
102
|
+
elsif @types[type]
|
103
|
+
@types[type]
|
104
|
+
elsif type.to_s.include? ?*
|
105
|
+
:pointer
|
106
|
+
elsif ((mod || FFI).find_type(type.to_sym) rescue false)
|
107
|
+
type.to_sym
|
108
|
+
else
|
109
|
+
raise "type #{type} not supported"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def shared_object
|
114
|
+
@compiler.compile(@code, @libraries)
|
115
|
+
end
|
116
|
+
|
117
|
+
def signatures
|
118
|
+
@signatures
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
def strip_comments (code)
|
123
|
+
code.gsub(%r(\s*/\*.*?\*/)m, '').
|
124
|
+
gsub(%r(^\s*//.*?\n), '').
|
125
|
+
gsub(%r([ \t]*//[^\n]*), '')
|
126
|
+
end
|
127
|
+
|
128
|
+
def parse_signature (code)
|
129
|
+
sig = strip_comments(code)
|
130
|
+
|
131
|
+
sig.gsub!(/^\s*\#.*(\\\n.*)*/, '') # strip preprocessor directives
|
132
|
+
sig.gsub!(/\s*\{.*/m, '') # strip function body
|
133
|
+
sig.gsub!(/\s+/, ' ') # clean and collapse whitespace
|
134
|
+
sig.gsub!(/\s*\*\s*/, ' * ') # clean pointers
|
135
|
+
sig.gsub!(/\s*const\s*/, '') # remove const
|
136
|
+
sig.strip!
|
137
|
+
|
138
|
+
whole, return_type, function_name, arg_string = sig.match(/(.*?(?:\ \*)?)\s*(\w+)\s*\(([^)]*)\)/).to_a
|
139
|
+
|
140
|
+
raise SyntaxError, "cannot parse signature: #{sig}" unless whole
|
141
|
+
|
142
|
+
args = arg_string.split(',').map {|arg|
|
143
|
+
# helps normalize into 'char * varname' form
|
144
|
+
arg = arg.gsub(/\s*\*\s*/, ' * ').strip
|
145
|
+
|
146
|
+
whole, type = arg.gsub(/\s*\*\s*/, ' * ').strip.match(/(((.*?(?:\ \*)?)\s*\*?)+)\s+(\w+)\s*$/).to_a
|
147
|
+
|
148
|
+
type
|
149
|
+
}
|
150
|
+
|
151
|
+
Signature.new(return_type, function_name, args, args.empty? ? -1 : args.length)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end; end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'ffi/inline/builders/c'
|
12
|
+
require 'ffi/inline/compilers/gxx'
|
13
|
+
|
14
|
+
module FFI; module Inline
|
15
|
+
|
16
|
+
Builder.define Builder[:c], :cplusplus, :cxx, :cpp, 'c++' do
|
17
|
+
def initialize (code = '', options = {})
|
18
|
+
super(code, options) rescue nil
|
19
|
+
|
20
|
+
use_compiler options[:use_compiler] || options[:compiler] || :gxx
|
21
|
+
end
|
22
|
+
|
23
|
+
def function (code, signature = nil)
|
24
|
+
parsed = parse_signature(code)
|
25
|
+
|
26
|
+
if signature
|
27
|
+
parsed[:arguments] = signature[:arguments] if signature[:arguments]
|
28
|
+
parsed[:return] = signature[:return] if signature[:return]
|
29
|
+
parsed[:blocking] = signature[:blocking] if signature[:blocking]
|
30
|
+
end
|
31
|
+
|
32
|
+
@signatures << parsed
|
33
|
+
|
34
|
+
raw %{extern "C" {#{code} }\n}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end; end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module FFI; module Inline
|
12
|
+
|
13
|
+
class Compiler
|
14
|
+
Extension = case RbConfig::CONFIG['target_os']
|
15
|
+
when /darwin/ then 'dylib'
|
16
|
+
when /mswin|mingw/ then 'dll'
|
17
|
+
else 'so'
|
18
|
+
end
|
19
|
+
|
20
|
+
@compilers = []
|
21
|
+
|
22
|
+
def self.[] (name)
|
23
|
+
return name if name.is_a?(Compiler)
|
24
|
+
|
25
|
+
@compilers.find {|compiler|
|
26
|
+
compiler.name.downcase == name.downcase ||
|
27
|
+
compiler.aliases.any? { |ali| ali.downcase == name.downcase }
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.define (name, *aliases, &block)
|
32
|
+
inherit_from = self
|
33
|
+
|
34
|
+
if name.is_a?(Compiler)
|
35
|
+
name = name.class
|
36
|
+
end
|
37
|
+
|
38
|
+
if name.is_a?(Class)
|
39
|
+
inherit_from = name
|
40
|
+
name = aliases.shift
|
41
|
+
end
|
42
|
+
|
43
|
+
@compilers << Class.new(inherit_from, &block).new(name, *aliases)
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :name, :aliases
|
47
|
+
attr_accessor :options
|
48
|
+
|
49
|
+
def initialize (name, *aliases)
|
50
|
+
@name = name
|
51
|
+
@aliases = aliases
|
52
|
+
end
|
53
|
+
|
54
|
+
def exists?
|
55
|
+
raise 'the Compiler has not been specialized'
|
56
|
+
end
|
57
|
+
|
58
|
+
def compile
|
59
|
+
raise 'the Compiler has not been specialized'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end; end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module FFI; module Inline
|
12
|
+
|
13
|
+
Compiler.define :gcc do
|
14
|
+
def exists?
|
15
|
+
`gcc -v 2>&1'`; $?.success?
|
16
|
+
end
|
17
|
+
|
18
|
+
def compile (code, libraries = [])
|
19
|
+
@code = code
|
20
|
+
@libraries = libraries
|
21
|
+
|
22
|
+
return output if File.exists?(output)
|
23
|
+
|
24
|
+
unless system(if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
25
|
+
"sh -c '#{ldshared} #{ENV['CFLAGS']} -o #{output.shellescape} #{input.shellescape} #{libs}' 2>#{log.shellescape}"
|
26
|
+
else
|
27
|
+
"#{ldshared} #{ENV['CFLAGS']} -o #{output.shellescape} #{input.shellescape} #{libs} 2>#{log.shellescape}"
|
28
|
+
end)
|
29
|
+
raise CompilationError.new(log)
|
30
|
+
end
|
31
|
+
|
32
|
+
output
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def digest
|
37
|
+
Digest::SHA1.hexdigest(@code + @libraries.to_s + @options.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def input
|
41
|
+
File.join(Inline.directory, "#{digest}.c").tap {|path|
|
42
|
+
File.open(path, 'w') { |f| f.write(@code) } unless File.exists?(path)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def output
|
47
|
+
File.join(Inline.directory, "#{digest}.#{Compiler::Extension}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def log
|
51
|
+
File.join(Inline.directory, "#{digest}.log")
|
52
|
+
end
|
53
|
+
|
54
|
+
def ldshared
|
55
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin/
|
56
|
+
"gcc -dynamic -bundle -fPIC #{options} #{ENV['LDFLAGS']}"
|
57
|
+
else
|
58
|
+
"gcc -shared -fPIC #{options} #{ENV['LDFLAGS']}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def libs
|
63
|
+
@libraries.map { |lib| "-l#{lib}".shellescape }.join(' ')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end; end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module FFI; module Inline
|
12
|
+
|
13
|
+
Compiler.define Compiler[:gcc], :gxx, 'g++' do
|
14
|
+
def exists?
|
15
|
+
`g++ -v 2>&1'`; $?.success?
|
16
|
+
end
|
17
|
+
|
18
|
+
def input
|
19
|
+
File.join(Inline.directory, "#{digest}.cpp").tap {|path|
|
20
|
+
File.open(path, 'w') { |f| f.write(@code) } unless File.exists?(path)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def ldshared
|
25
|
+
if RbConfig::CONFIG['target_os'] =~ /darwin/
|
26
|
+
"g++ -dynamic -bundle -fPIC #{options} #{ENV['LDFLAGS']}"
|
27
|
+
else
|
28
|
+
"g++ -shared -fPIC #{options} #{ENV['LDFLAGS']}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end; end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module FFI; module Inline
|
12
|
+
|
13
|
+
Compiler.define :tcc do
|
14
|
+
def exists?
|
15
|
+
`tcc -v 2>&1'`; $?.success?
|
16
|
+
end
|
17
|
+
|
18
|
+
def compile (code, libraries = [])
|
19
|
+
@code = code
|
20
|
+
@libraries = libraries
|
21
|
+
|
22
|
+
return output if File.exists?(output)
|
23
|
+
|
24
|
+
unless system(if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
25
|
+
"sh -c '#{ldshared} #{ENV['CFLAGS']} #{libs} -o #{output.shellescape} #{input.shellescape}' 2>#{log.shellescape}"
|
26
|
+
else
|
27
|
+
"#{ldshared} #{ENV['CFLAGS']} #{libs} -o #{output.shellescape} #{input.shellescape} 2>#{log.shellescape}"
|
28
|
+
end)
|
29
|
+
raise CompilationError.new(log)
|
30
|
+
end
|
31
|
+
|
32
|
+
output
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def digest
|
37
|
+
Digest::SHA1.hexdigest(@code + @libraries.to_s + @options.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
def input
|
41
|
+
File.join(Inline.directory, "#{digest}.c").tap {|path|
|
42
|
+
File.open(path, 'w') { |f| f.write(@code) } unless File.exists?(path)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def output
|
47
|
+
File.join(Inline.directory, "#{digest}.#{Compiler::Extension}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def log
|
51
|
+
File.join(Inline.directory, "#{digest}.log")
|
52
|
+
end
|
53
|
+
|
54
|
+
def ldshared
|
55
|
+
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
56
|
+
"tcc -rdynamic -shared -fPIC #{options} #{ENV['LDFLAGS']}"
|
57
|
+
else
|
58
|
+
"tcc -shared #{options} #{ENV['LDFLAGS']}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def libs
|
63
|
+
@libraries.map { |lib| "-l#{lib}".shellescape }.join(' ')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end; end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
class CompilationError < RuntimeError
|
12
|
+
def initialize (path)
|
13
|
+
@path = path
|
14
|
+
|
15
|
+
super "compile error: see logs at #{@path}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def log
|
19
|
+
File.read(@path)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'ffi/inline/error'
|
12
|
+
|
13
|
+
module FFI
|
14
|
+
|
15
|
+
module Inline
|
16
|
+
def self.directory
|
17
|
+
if ENV['FFI_INLINER_PATH'] && !ENV['FFI_INLINER_PATH'].empty?
|
18
|
+
@directory = ENV['FFI_INLINER_PATH']
|
19
|
+
else
|
20
|
+
require 'tmpdir'
|
21
|
+
@directory ||= File.expand_path(File.join(Dir.tmpdir, ".ffi-inline-#{Process.uid}"))
|
22
|
+
end
|
23
|
+
|
24
|
+
if File.exists?(@directory) && !File.directory?(@directory)
|
25
|
+
raise 'the FFI_INLINER_PATH exists and is not a directory'
|
26
|
+
end
|
27
|
+
|
28
|
+
if !File.exists?(@directory)
|
29
|
+
FileUtils.mkdir(@directory)
|
30
|
+
end
|
31
|
+
|
32
|
+
@directory
|
33
|
+
end
|
34
|
+
|
35
|
+
def inline (*args, &block)
|
36
|
+
if self.class == Class
|
37
|
+
instance_inline(*args, &block)
|
38
|
+
else
|
39
|
+
singleton_inline(*args, &block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def singleton_inline (*args)
|
44
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
45
|
+
|
46
|
+
language, code = if args.length == 2
|
47
|
+
args
|
48
|
+
else
|
49
|
+
block_given? ? [args.shift || :c, ''] : [:c, args.shift || '']
|
50
|
+
end
|
51
|
+
|
52
|
+
builder = Builder[language].new(code, options)
|
53
|
+
yield builder if block_given?
|
54
|
+
mod = builder.build
|
55
|
+
|
56
|
+
builder.symbols.each {|sym|
|
57
|
+
define_singleton_method sym, &mod.method(sym)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def instance_inline (*args)
|
62
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
63
|
+
|
64
|
+
language, code = if args.length == 2
|
65
|
+
args
|
66
|
+
else
|
67
|
+
block_given? ? [args.shift || :c, ''] : [:c, args.shift || '']
|
68
|
+
end
|
69
|
+
|
70
|
+
builder = Builder[language].new(code, options)
|
71
|
+
yield builder if block_given?
|
72
|
+
mod = builder.build
|
73
|
+
|
74
|
+
builder.symbols.each {|sym|
|
75
|
+
define_method sym, &mod.method(sym)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
require 'ffi/inline/compilers'
|
83
|
+
require 'ffi/inline/builders'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module FFI
|
12
|
+
|
13
|
+
module Inline
|
14
|
+
VERSION = '0.0.4'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-inline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -68,17 +68,17 @@ files:
|
|
68
68
|
- Rakefile
|
69
69
|
- examples/ex_1.rb
|
70
70
|
- ffi-inline.gemspec
|
71
|
-
- lib/ffi/
|
72
|
-
- lib/ffi/
|
73
|
-
- lib/ffi/
|
74
|
-
- lib/ffi/
|
75
|
-
- lib/ffi/
|
76
|
-
- lib/ffi/
|
77
|
-
- lib/ffi/
|
78
|
-
- lib/ffi/
|
79
|
-
- lib/ffi/
|
80
|
-
- lib/ffi/
|
81
|
-
- lib/ffi/
|
71
|
+
- lib/ffi/inline.rb
|
72
|
+
- lib/ffi/inline/builders.rb
|
73
|
+
- lib/ffi/inline/builders/c.rb
|
74
|
+
- lib/ffi/inline/builders/cpp.rb
|
75
|
+
- lib/ffi/inline/compilers.rb
|
76
|
+
- lib/ffi/inline/compilers/gcc.rb
|
77
|
+
- lib/ffi/inline/compilers/gxx.rb
|
78
|
+
- lib/ffi/inline/compilers/tcc.rb
|
79
|
+
- lib/ffi/inline/error.rb
|
80
|
+
- lib/ffi/inline/inline.rb
|
81
|
+
- lib/ffi/inline/version.rb
|
82
82
|
- spec/inliner_spec.rb
|
83
83
|
homepage: http://github.com/meh/ruby-ffi-inline
|
84
84
|
licenses: []
|
data/lib/ffi/inliner.rb
DELETED
data/lib/ffi/inliner/builders.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
module FFI; module Inliner
|
2
|
-
|
3
|
-
Signature = ::Struct.new(:return, :name, :arguments, :arity, :blocking)
|
4
|
-
|
5
|
-
class Builder
|
6
|
-
@builders = []
|
7
|
-
|
8
|
-
def self.[](name)
|
9
|
-
return name if name.is_a?(Builder)
|
10
|
-
|
11
|
-
@builders.find {|builder|
|
12
|
-
builder.name.downcase == name.downcase ||
|
13
|
-
builder.aliases.any? { |ali| ali.downcase == name.downcase }
|
14
|
-
}
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.define (name, *aliases, &block)
|
18
|
-
inherit_from = self
|
19
|
-
|
20
|
-
if name.is_a?(Builder)
|
21
|
-
name = name.class
|
22
|
-
end
|
23
|
-
|
24
|
-
if name.is_a?(Class)
|
25
|
-
inherit_from = name
|
26
|
-
name = aliases.shift
|
27
|
-
end
|
28
|
-
|
29
|
-
@builders << Class.new(inherit_from, &block).tap {|k|
|
30
|
-
k.instance_eval {
|
31
|
-
define_singleton_method :name do name end
|
32
|
-
define_singleton_method :aliases do aliases end
|
33
|
-
}
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
attr_reader :code, :compiler
|
38
|
-
|
39
|
-
def initialize(code = "")
|
40
|
-
@code = code
|
41
|
-
@evals = []
|
42
|
-
end
|
43
|
-
|
44
|
-
def use_compiler(compiler)
|
45
|
-
@compiler = Compiler[compiler]
|
46
|
-
end
|
47
|
-
|
48
|
-
def raw(code)
|
49
|
-
@code << code
|
50
|
-
end
|
51
|
-
|
52
|
-
def eval(&block)
|
53
|
-
@evals << block
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_ffi_type(type)
|
57
|
-
raise 'the Builder has not been specialized'
|
58
|
-
end
|
59
|
-
|
60
|
-
def shared_object
|
61
|
-
raise 'the Builder has not been specialized'
|
62
|
-
end
|
63
|
-
|
64
|
-
def signatures
|
65
|
-
raise 'the Builder has not been specialized'
|
66
|
-
end
|
67
|
-
|
68
|
-
def symbols
|
69
|
-
signatures.map { |s| s.name.to_sym }
|
70
|
-
end
|
71
|
-
|
72
|
-
def build
|
73
|
-
builder = self
|
74
|
-
blocks = @evals
|
75
|
-
|
76
|
-
mod = Module.new
|
77
|
-
mod.instance_eval {
|
78
|
-
extend FFI::Library
|
79
|
-
|
80
|
-
ffi_lib builder.shared_object
|
81
|
-
|
82
|
-
blocks.each { |block| instance_eval &block }
|
83
|
-
|
84
|
-
builder.signatures.each {|s|
|
85
|
-
attach_function s.name, s.arguments.compact.map {|a|
|
86
|
-
builder.to_ffi_type(a, self)
|
87
|
-
}, builder.to_ffi_type(s.return, self), :blocking => s.blocking
|
88
|
-
}
|
89
|
-
}
|
90
|
-
|
91
|
-
mod
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
end; end
|
96
|
-
|
97
|
-
require 'ffi/inliner/builders/c'
|
98
|
-
require 'ffi/inliner/builders/cpp'
|
@@ -1,147 +0,0 @@
|
|
1
|
-
require 'ffi/inliner/compilers/tcc'
|
2
|
-
require 'ffi/inliner/compilers/gcc'
|
3
|
-
|
4
|
-
module FFI; module Inliner
|
5
|
-
|
6
|
-
Builder.define :c do
|
7
|
-
ToFFI = {
|
8
|
-
'void' => :void,
|
9
|
-
'char' => :char,
|
10
|
-
'unsigned char' => :uchar,
|
11
|
-
'int' => :int,
|
12
|
-
'unsigned int' => :uint,
|
13
|
-
'long' => :long,
|
14
|
-
'unsigned long' => :ulong,
|
15
|
-
'float' => :float,
|
16
|
-
'double' => :double,
|
17
|
-
}
|
18
|
-
|
19
|
-
attr_reader :code, :compiler, :libraries
|
20
|
-
|
21
|
-
def initialize(code = nil, options = {})
|
22
|
-
super(code)
|
23
|
-
|
24
|
-
@types = ToFFI.dup
|
25
|
-
@libraries = options[:libraries] || []
|
26
|
-
|
27
|
-
@signatures = []
|
28
|
-
|
29
|
-
use_compiler options[:use_compiler] || options[:compiler] || :gcc
|
30
|
-
|
31
|
-
@signatures << parse_signature(code) if code && !code.empty?
|
32
|
-
end
|
33
|
-
|
34
|
-
def libraries(*libraries)
|
35
|
-
@libraries.concat(libraries)
|
36
|
-
end
|
37
|
-
|
38
|
-
def types(map = nil)
|
39
|
-
map ? @types.merge!(map) : @types
|
40
|
-
end; alias map types
|
41
|
-
|
42
|
-
def raw(code, no_line = false)
|
43
|
-
return super(code) if no_line
|
44
|
-
|
45
|
-
whole, path, line = caller.find { |line| line !~ /ffi-inliner/ }.match(/^(.*?):(\d+):in/).to_a
|
46
|
-
|
47
|
-
super "\n#line #{line.to_i} #{path.inspect}\n" << code
|
48
|
-
end; alias c_raw raw
|
49
|
-
|
50
|
-
def include(path, options = {})
|
51
|
-
delimiter = (options[:quoted] || options[:local]) ? ['"', '"'] : ['<', '>']
|
52
|
-
|
53
|
-
raw "#include #{delimiter.first}#{path}#{delimiter.last}\n", true
|
54
|
-
end
|
55
|
-
|
56
|
-
def typedef (from, to)
|
57
|
-
raw "typedef #{from} #{to};"
|
58
|
-
end
|
59
|
-
|
60
|
-
def function(code, signature = nil)
|
61
|
-
parsed = parse_signature(code)
|
62
|
-
|
63
|
-
if signature
|
64
|
-
parsed[:arguments] = signature[:arguments] if signature[:arguments]
|
65
|
-
parsed[:return] = signature[:return] if signature[:return]
|
66
|
-
parsed[:blocking] = signature[:blocking] if signature[:blocking]
|
67
|
-
end
|
68
|
-
|
69
|
-
@signatures << parsed
|
70
|
-
|
71
|
-
raw code
|
72
|
-
end; alias c function
|
73
|
-
|
74
|
-
def struct(ffi_struct)
|
75
|
-
raw %{
|
76
|
-
typedef struct {#{
|
77
|
-
ffi_struct.layout.fields.map {|field|
|
78
|
-
"#{field} #{field.name};"
|
79
|
-
}.join("\n")
|
80
|
-
}} #{ffi_struct.class.name}
|
81
|
-
}, true
|
82
|
-
end
|
83
|
-
|
84
|
-
def to_ffi_type(type, mod = nil)
|
85
|
-
raise ArgumentError, 'type is nil' if type.nil?
|
86
|
-
|
87
|
-
if type.is_a?(Symbol) || type.is_a?(FFI::Type) || (type.is_a?(Class) && type.ancestors.include?(FFI::Struct))
|
88
|
-
type
|
89
|
-
elsif @types[type]
|
90
|
-
@types[type]
|
91
|
-
elsif type.to_s.include? ?*
|
92
|
-
:pointer
|
93
|
-
elsif ((mod || FFI).find_type(type.to_sym) rescue false)
|
94
|
-
type.to_sym
|
95
|
-
else
|
96
|
-
raise "type #{type} not supported"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def shared_object
|
101
|
-
@compiler.compile(@code, @libraries)
|
102
|
-
end
|
103
|
-
|
104
|
-
def signatures
|
105
|
-
@signatures
|
106
|
-
end
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
# Based on RubyInline code by Ryan Davis
|
111
|
-
# Copyright (c) 2001-2007 Ryan Davis, Zen Spider Software
|
112
|
-
def strip_comments(code)
|
113
|
-
code.gsub(%r(\s*/\*.*?\*/)m, '').
|
114
|
-
gsub(%r(^\s*//.*?\n), '').
|
115
|
-
gsub(%r([ \t]*//[^\n]*), '')
|
116
|
-
end
|
117
|
-
|
118
|
-
# Based on RubyInline code by Ryan Davis
|
119
|
-
# Copyright (c) 2001-2007 Ryan Davis, Zen Spider Software
|
120
|
-
def parse_signature(code)
|
121
|
-
sig = strip_comments(code)
|
122
|
-
|
123
|
-
sig.gsub!(/^\s*\#.*(\\\n.*)*/, '') # strip preprocessor directives
|
124
|
-
sig.gsub!(/\s*\{.*/m, '') # strip function body
|
125
|
-
sig.gsub!(/\s+/, ' ') # clean and collapse whitespace
|
126
|
-
sig.gsub!(/\s*\*\s*/, ' * ') # clean pointers
|
127
|
-
sig.gsub!(/\s*const\s*/, '') # remove const
|
128
|
-
sig.strip!
|
129
|
-
|
130
|
-
whole, return_type, function_name, arg_string = sig.match(/(.*?(?:\ \*)?)\s*(\w+)\s*\(([^)]*)\)/).to_a
|
131
|
-
|
132
|
-
raise SyntaxError, "cannot parse signature: #{sig}" unless whole
|
133
|
-
|
134
|
-
args = arg_string.split(',').map {|arg|
|
135
|
-
# helps normalize into 'char * varname' form
|
136
|
-
arg = arg.gsub(/\s*\*\s*/, ' * ').strip
|
137
|
-
|
138
|
-
whole, type = arg.gsub(/\s*\*\s*/, ' * ').strip.match(/(((.*?(?:\ \*)?)\s*\*?)+)\s+(\w+)\s*$/).to_a
|
139
|
-
|
140
|
-
type
|
141
|
-
}
|
142
|
-
|
143
|
-
Signature.new(return_type, function_name, args, args.empty? ? -1 : args.length)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
end; end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'ffi/inliner/builders/c'
|
2
|
-
require 'ffi/inliner/compilers/gxx'
|
3
|
-
|
4
|
-
module FFI; module Inliner
|
5
|
-
|
6
|
-
Builder.define Builder[:c], :cplusplus, :cxx, :cpp, 'c++' do
|
7
|
-
def initialize(code = "", options = {})
|
8
|
-
super(code, options) rescue nil
|
9
|
-
|
10
|
-
use_compiler options[:use_compiler] || options[:compiler] || :gxx
|
11
|
-
end
|
12
|
-
|
13
|
-
def function(code, signature = nil)
|
14
|
-
parsed = parse_signature(code)
|
15
|
-
|
16
|
-
if signature
|
17
|
-
parsed[:arguments] = signature[:arguments] if signature[:arguments]
|
18
|
-
parsed[:return] = signature[:return] if signature[:return]
|
19
|
-
parsed[:blocking] = signature[:blocking] if signature[:blocking]
|
20
|
-
end
|
21
|
-
|
22
|
-
@signatures << parsed
|
23
|
-
|
24
|
-
raw 'extern "C" {' << code << '}' << "\n"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
end; end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module FFI; module Inliner
|
2
|
-
|
3
|
-
class Compiler
|
4
|
-
Extension = case RbConfig::CONFIG['target_os']
|
5
|
-
when /darwin/ then 'dylib'
|
6
|
-
when /mswin|mingw/ then 'dll'
|
7
|
-
else 'so'
|
8
|
-
end
|
9
|
-
|
10
|
-
@compilers = []
|
11
|
-
|
12
|
-
def self.[] (name)
|
13
|
-
return name if name.is_a?(Compiler)
|
14
|
-
|
15
|
-
@compilers.find {|compiler|
|
16
|
-
compiler.name.downcase == name.downcase ||
|
17
|
-
compiler.aliases.any? { |ali| ali.downcase == name.downcase }
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.define (name, *aliases, &block)
|
22
|
-
inherit_from = self
|
23
|
-
|
24
|
-
if name.is_a?(Compiler)
|
25
|
-
name = name.class
|
26
|
-
end
|
27
|
-
|
28
|
-
if name.is_a?(Class)
|
29
|
-
inherit_from = name
|
30
|
-
name = aliases.shift
|
31
|
-
end
|
32
|
-
|
33
|
-
@compilers << Class.new(inherit_from, &block).new(name, *aliases)
|
34
|
-
end
|
35
|
-
|
36
|
-
attr_reader :name, :aliases
|
37
|
-
attr_accessor :options
|
38
|
-
|
39
|
-
def initialize(name, *aliases)
|
40
|
-
@name = name
|
41
|
-
@aliases = aliases
|
42
|
-
end
|
43
|
-
|
44
|
-
def exists?
|
45
|
-
raise 'the Compiler has not been specialized'
|
46
|
-
end
|
47
|
-
|
48
|
-
def compile
|
49
|
-
raise 'the Compiler has not been specialized'
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end; end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module FFI; module Inliner
|
2
|
-
|
3
|
-
Compiler.define :gcc do
|
4
|
-
def exists?
|
5
|
-
`gcc -v 2>&1'`; $?.success?
|
6
|
-
end
|
7
|
-
|
8
|
-
def compile (code, libraries = [])
|
9
|
-
@code = code
|
10
|
-
@libraries = libraries
|
11
|
-
|
12
|
-
return output if File.exists?(output)
|
13
|
-
|
14
|
-
unless system(if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
15
|
-
"sh -c '#{ldshared} #{ENV['CFLAGS']} -o #{output.shellescape} #{input.shellescape} #{libs}' 2>#{log.shellescape}"
|
16
|
-
else
|
17
|
-
"#{ldshared} #{ENV['CFLAGS']} -o #{output.shellescape} #{input.shellescape} #{libs} 2>#{log.shellescape}"
|
18
|
-
end)
|
19
|
-
raise CompilationError.new(log)
|
20
|
-
end
|
21
|
-
|
22
|
-
output
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def digest
|
27
|
-
Digest::SHA1.hexdigest(@code + @libraries.to_s + @options.to_s)
|
28
|
-
end
|
29
|
-
|
30
|
-
def input
|
31
|
-
File.join(Inliner.directory, "#{digest}.c").tap {|path|
|
32
|
-
File.open(path, 'w') { |f| f.write(@code) } unless File.exists?(path)
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
def output
|
37
|
-
File.join(Inliner.directory, "#{digest}.#{Compiler::Extension}")
|
38
|
-
end
|
39
|
-
|
40
|
-
def log
|
41
|
-
File.join(Inliner.directory, "#{digest}.log")
|
42
|
-
end
|
43
|
-
|
44
|
-
def ldshared
|
45
|
-
if RbConfig::CONFIG['target_os'] =~ /darwin/
|
46
|
-
"gcc -dynamic -bundle -fPIC #{options} #{ENV['LDFLAGS']}"
|
47
|
-
else
|
48
|
-
"gcc -shared -fPIC #{options} #{ENV['LDFLAGS']}"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def libs
|
53
|
-
@libraries.map { |lib| "-l#{lib}".shellescape }.join(' ')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end; end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module FFI; module Inliner
|
2
|
-
|
3
|
-
Compiler.define Compiler[:gcc], :gxx, 'g++' do
|
4
|
-
def exists?
|
5
|
-
`g++ -v 2>&1'`; $?.success?
|
6
|
-
end
|
7
|
-
|
8
|
-
def input
|
9
|
-
File.join(Inliner.directory, "#{digest}.cpp").tap {|path|
|
10
|
-
File.open(path, 'w') { |f| f.write(@code) } unless File.exists?(path)
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
def ldshared
|
15
|
-
if RbConfig::CONFIG['target_os'] =~ /darwin/
|
16
|
-
"g++ -dynamic -bundle -fPIC #{options} #{ENV['LDFLAGS']}"
|
17
|
-
else
|
18
|
-
"g++ -shared -fPIC #{options} #{ENV['LDFLAGS']}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
end; end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module FFI; module Inliner
|
2
|
-
|
3
|
-
Compiler.define :tcc do
|
4
|
-
def exists?
|
5
|
-
`tcc -v 2>&1'`; $?.success?
|
6
|
-
end
|
7
|
-
|
8
|
-
def compile (code, libraries = [])
|
9
|
-
@code = code
|
10
|
-
@libraries = libraries
|
11
|
-
|
12
|
-
return output if File.exists?(output)
|
13
|
-
|
14
|
-
unless system(if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
15
|
-
"sh -c '#{ldshared} #{ENV['CFLAGS']} #{libs} -o #{output.shellescape} #{input.shellescape}' 2>#{log.shellescape}"
|
16
|
-
else
|
17
|
-
"#{ldshared} #{ENV['CFLAGS']} #{libs} -o #{output.shellescape} #{input.shellescape} 2>#{log.shellescape}"
|
18
|
-
end)
|
19
|
-
raise CompilationError.new(log)
|
20
|
-
end
|
21
|
-
|
22
|
-
output
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def digest
|
27
|
-
Digest::SHA1.hexdigest(@code + @libraries.to_s + @options.to_s)
|
28
|
-
end
|
29
|
-
|
30
|
-
def input
|
31
|
-
File.join(Inliner.directory, "#{digest}.c").tap {|path|
|
32
|
-
File.open(path, 'w') { |f| f.write(@code) } unless File.exists?(path)
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
def output
|
37
|
-
File.join(Inliner.directory, "#{digest}.#{Compiler::Extension}")
|
38
|
-
end
|
39
|
-
|
40
|
-
def log
|
41
|
-
File.join(Inliner.directory, "#{digest}.log")
|
42
|
-
end
|
43
|
-
|
44
|
-
def ldshared
|
45
|
-
if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/
|
46
|
-
"tcc -rdynamic -shared -fPIC #{options} #{ENV['LDFLAGS']}"
|
47
|
-
else
|
48
|
-
"tcc -shared #{options} #{ENV['LDFLAGS']}"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def libs
|
53
|
-
@libraries.map { |lib| "-l#{lib}".shellescape }.join(' ')
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end; end
|
data/lib/ffi/inliner/error.rb
DELETED
data/lib/ffi/inliner/inliner.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
require 'ffi/inliner/error'
|
2
|
-
|
3
|
-
module FFI
|
4
|
-
|
5
|
-
module Inliner
|
6
|
-
def self.directory
|
7
|
-
if ENV['FFI_INLINER_PATH'] && !ENV['FFI_INLINER_PATH'].empty?
|
8
|
-
@directory = ENV['FFI_INLINER_PATH']
|
9
|
-
else
|
10
|
-
require 'tmpdir'
|
11
|
-
@directory ||= File.expand_path(File.join(Dir.tmpdir, ".ffi-inliner-#{Process.uid}"))
|
12
|
-
end
|
13
|
-
|
14
|
-
if File.exists?(@directory) && !File.directory?(@directory)
|
15
|
-
raise 'the FFI_INLINER_PATH exists and is not a directory'
|
16
|
-
end
|
17
|
-
|
18
|
-
if !File.exists?(@directory)
|
19
|
-
FileUtils.mkdir(@directory)
|
20
|
-
end
|
21
|
-
|
22
|
-
@directory
|
23
|
-
end
|
24
|
-
|
25
|
-
def inline(*args, &block)
|
26
|
-
if self.class == Class
|
27
|
-
instance_inline(*args, &block)
|
28
|
-
else
|
29
|
-
singleton_inline(*args, &block)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def singleton_inline(*args)
|
34
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
35
|
-
|
36
|
-
language, code = if args.length == 2
|
37
|
-
args
|
38
|
-
else
|
39
|
-
block_given? ? [args.shift || :c, ''] : [:c, args.shift || '']
|
40
|
-
end
|
41
|
-
|
42
|
-
builder = Builder[language].new(code, options)
|
43
|
-
yield builder if block_given?
|
44
|
-
mod = builder.build
|
45
|
-
|
46
|
-
builder.symbols.each {|sym|
|
47
|
-
define_singleton_method sym, &mod.method(sym)
|
48
|
-
}
|
49
|
-
end
|
50
|
-
|
51
|
-
def instance_inline(*args)
|
52
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
53
|
-
|
54
|
-
language, code = if args.length == 2
|
55
|
-
args
|
56
|
-
else
|
57
|
-
block_given? ? [args.shift || :c, ''] : [:c, args.shift || '']
|
58
|
-
end
|
59
|
-
|
60
|
-
builder = Builder[language].new(code, options)
|
61
|
-
yield builder if block_given?
|
62
|
-
mod = builder.build
|
63
|
-
|
64
|
-
builder.symbols.each {|sym|
|
65
|
-
define_method sym, &mod.method(sym)
|
66
|
-
}
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
require 'ffi/inliner/compilers'
|
73
|
-
require 'ffi/inliner/builders'
|