Class: Rbind::Rbind

Inherits:
Object
  • Object
show all
Defined in:
lib/rbind/rbind.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Rbind

Returns a new instance of Rbind.



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rbind/rbind.rb', line 49

def initialize(name)
    @name = name
    @includes = []
    @pkg_config = []
    @gems = []
    @parser = DefaultParser.new
    lib_name = "rbind_#{name.downcase}"
    @generator_c = GeneratorC.new(@parser,lib_name)
    @generator_ruby = GeneratorRuby.new(@parser,name,lib_name)
    @generator_extern = GeneratorExtern.new(@parser)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args) ⇒ Object



199
200
201
202
203
204
205
206
207
# File 'lib/rbind/rbind.rb', line 199

def method_missing(m,*args)
    t = @parser.type(m.to_s,false,false)
    return t if t

    op = @parser.operation(m.to_s,false)
    return op if op

    super
end

Instance Attribute Details

#gemsObject

Returns the value of attribute gems.



11
12
13
# File 'lib/rbind/rbind.rb', line 11

def gems
  @gems
end

#generator_cObject

Returns the value of attribute generator_c.



6
7
8
# File 'lib/rbind/rbind.rb', line 6

def generator_c
  @generator_c
end

#generator_rubyObject

Returns the value of attribute generator_ruby.



7
8
9
# File 'lib/rbind/rbind.rb', line 7

def generator_ruby
  @generator_ruby
end

#includesObject

Returns the value of attribute includes.



8
9
10
# File 'lib/rbind/rbind.rb', line 8

def includes
  @includes
end

#nameObject

Returns the value of attribute name.



9
10
11
# File 'lib/rbind/rbind.rb', line 9

def name
  @name
end

#parserObject

Returns the value of attribute parser.



5
6
7
# File 'lib/rbind/rbind.rb', line 5

def parser
  @parser
end

#pkg_configObject

Returns the value of attribute pkg_config.



10
11
12
# File 'lib/rbind/rbind.rb', line 10

def pkg_config
  @pkg_config
end

Class Method Details

.gem_path(gem_name) ⇒ Object



28
29
30
31
32
33
34
35
# File 'lib/rbind/rbind.rb', line 28

def self.gem_path(gem_name)
    # TODO use gem api
    out = IO.popen("gem contents #{gem_name}")
    out.readlines.each do |line|
        return $1 if line =~ /(.*)extern.rbind/
    end
    raise "Cannot find paths for gem #{gem_name}"
end

.pkg_paths(pkg_name) ⇒ Object



13
14
15
16
17
18
19
20
# File 'lib/rbind/rbind.rb', line 13

def self.pkg_paths(pkg_name)
    out = IO.popen("pkg-config --cflags-only-I #{pkg_name}")
    paths = out.read.split("-I").delete_if(&:empty?).map do |i|
        i.gsub("\n","").gsub(" ","")
    end
    raise "Cannot find pkg paths for #{pkg_name}" if paths.empty?
    paths
end

.rbind_pkg_paths(pkg_names) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/rbind/rbind.rb', line 37

def self.rbind_pkg_paths(pkg_names)
    rbind_packages = rbind_pkgs(pkg_names)
    rbind_paths = rbind_packages.map do |pkg|
        paths = pkg_paths(pkg)
        path = paths.find do |p|
            File.exist?(File.join(p,pkg,"extern.rbind"))
        end
        raise "cannot find extern.rbind for rbind package #{pkg}" unless path
        File.join(path,pkg)
    end
end

.rbind_pkgs(pkg_names) ⇒ Object



22
23
24
25
26
# File 'lib/rbind/rbind.rb', line 22

def self.rbind_pkgs(pkg_names)
    pkg_names.find_all do |p|
        !!(p =~ /^rbind_.*/)
    end
end

Instance Method Details

#buildObject



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/rbind/rbind.rb', line 125

def build
    ::Rbind.log.info "build c wrappers"
    path = File.join(generator_c.output_path,"build")
    FileUtils.mkdir_p(path) if path && !File.directory?(path)
    Dir.chdir(path) do
        if !system("cmake -C ..")
            raise "CMake Configure Error"
        end
        if !system("make")
            raise "Make Build Error"
        end
    end
    if !system("cp #{File.join(path,"lib*.*")} #{generator_ruby.output_path}")
        raise "cannot copy library to #{generator_ruby.output_path}"
    end
    ::Rbind.log.info "all done !"
end

#check_pythonObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rbind/rbind.rb', line 67

def check_python
    out = IO.popen("which python")
    if(out.read.empty?)
        raise 'Cannot find python interpreter needed for parsing header files'
    end
    in_,out,err = Open3.popen3("python --version")
    str = err.read
    str = if str.empty?
              out.read
          else
              str
          end
    if(str =~ /[a-zA-Z]* (.*)/)
        if $1.to_f < 2.7
            raise "Wrong python version #{$1}. At least python 2.7 is needed for parsing header files"
        end
    else
        raise 'Cannot determine python version needed for parsing header files'
    end
end

#generate(c_path = "src", ruby_path = "ruby/lib/#{name.downcase}") ⇒ Object



143
144
145
146
147
# File 'lib/rbind/rbind.rb', line 143

def generate(c_path = "src",ruby_path = "ruby/lib/#{name.downcase}")
    generate_c c_path
    generate_extern c_path
    generate_ruby ruby_path
end

#generate_c(path) ⇒ Object



160
161
162
163
164
165
166
167
# File 'lib/rbind/rbind.rb', line 160

def generate_c(path)
    ::Rbind.log.info "generate c wrappers"
    @generator_c.includes += includes
    @generator_c.includes.uniq!
    @generator_c.pkg_config = pkg_config
    @generator_c.gems = gems
    @generator_c.generate(path)
end

#generate_extern(path) ⇒ Object



169
170
171
# File 'lib/rbind/rbind.rb', line 169

def generate_extern(path)
    @generator_extern.generate(path,@generator_ruby.module_name,@generator_ruby.file_prefix)
end

#generate_ruby(path) ⇒ Object



149
150
151
152
153
154
155
156
157
158
# File 'lib/rbind/rbind.rb', line 149

def generate_ruby(path)
    ::Rbind.log.info "generate ruby ffi wrappers"
    paths = Rbind.rbind_pkg_paths(@pkg_config)
    modules = paths.map do |pkg|
        config = YAML.load(File.open(File.join(pkg,"config.rbind")).read)
        config.file_prefix
    end
    @generator_ruby.required_module_names = modules + gems
    @generator_ruby.generate(path)
end

#import_std_stringObject



194
195
196
197
# File 'lib/rbind/rbind.rb', line 194

def import_std_string
    @generator_c.includes << "<string>"
    @parser.add_type(RString.new("std::string",@parser))
end

#libsObject



190
191
192
# File 'lib/rbind/rbind.rb', line 190

def libs
    @generator_c.libs
end

#on_type_not_found(&block) ⇒ Object



186
187
188
# File 'lib/rbind/rbind.rb', line 186

def on_type_not_found(&block)
    @parser.on_type_not_found(&block)
end

#parse(*files) ⇒ Object



61
62
63
64
65
# File 'lib/rbind/rbind.rb', line 61

def parse(*files)
    files.each do |path|
        parser.parse File.new(path).read
    end
end

#parse_externObject

parses other rbind packages



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/rbind/rbind.rb', line 89

def parse_extern
    paths = Rbind.rbind_pkg_paths(@pkg_config)
    paths.each do |pkg|
        config = YAML.load(File.open(File.join(pkg,"config.rbind")).read)
        path = File.join(pkg,"extern.rbind")
        ::Rbind.log.info "parsing extern rbind pkg file #{path}"
        parser.parse(File.open(path).read,config.ruby_module_name)
    end
    @gems.each do |gem|
        path = Rbind.gem_path(gem)
        config = YAML.load(File.open(File.join(path,"config.rbind")).read)
        path = File.join(path,"extern.rbind")
        ::Rbind.log.info "parsing extern gem file #{path}"
        parser.parse(File.open(path).read,config.ruby_module_name)
    end
end

#parse_headers(*headers) ⇒ Object



121
122
123
# File 'lib/rbind/rbind.rb', line 121

def parse_headers(*headers)
    parser.parse parse_headers_dry(*headers)
end

#parse_headers_dry(*headers) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/rbind/rbind.rb', line 106

def parse_headers_dry(*headers)
    check_python
    headers = if headers.empty?
                  includes
              else
                  headers
              end
    headers = headers.map do |h|
        "\"#{h}\""
    end
    path = File.join(File.dirname(__FILE__),'tools','hdr_parser.py')
    out = IO.popen("python #{path} #{headers.join(" ")}")
    out.read
end

#type(*args) ⇒ Object



182
183
184
# File 'lib/rbind/rbind.rb', line 182

def type(*args)
    parser.type(*args)
end

#use_namespace(name) ⇒ Object



173
174
175
176
177
178
179
180
# File 'lib/rbind/rbind.rb', line 173

def use_namespace(name)
    t = if name.is_a? String
            parser.type(name)
        else
            name
        end
    parser.use_namespace t
end