9 LinkerConfig = Struct.new(:libraries, :library_paths, :flags, :flags_before_libraries, :flags_after_libraries)
14 def_delegators :@build, :filename, :objfile, :libfile, :exefile
16 attr_accessor :name, :dir, :build
18 attr_accessor :build_config_initializer
20 attr_accessor :version
21 attr_accessor :description, :summary
22 attr_accessor :homepage
23 attr_accessor :licenses, :authors
24 alias :license= :licenses=
25 alias :author= :authors=
27 attr_accessor :rbfiles, :objs
28 attr_accessor :test_objs, :test_rbfiles, :test_args
29 attr_accessor :test_preload
33 attr_accessor :requirements
34 attr_reader :dependencies
36 attr_block MRuby::Build::COMMANDS
38 def initialize(name, &block)
42 MRuby::Gem.current = self
46 MRuby::Gem.current = self
47 @build.compilers.each do |compiler|
48 compiler.include_paths << "#{dir}/include"
50 MRuby::Build::COMMANDS.each do |command|
51 instance_variable_set("@#{command}", @build.send(command).clone)
53 @linker = LinkerConfig.new([], [], [], [])
55 @rbfiles = Dir.glob("#{dir}/mrblib/*.rb").sort
56 @objs = Dir.glob("#{dir}/src/*.{c,cpp,m,asm,S}").map do |f|
57 objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
59 @objs << objfile("#{build_dir}/gem_init")
61 @test_rbfiles = Dir.glob("#{dir}/test/*.rb")
62 @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,m,asm,S}").map do |f|
63 objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X"))
65 @test_preload = 'test/assert.rb'
73 instance_eval(&@initializer)
75 if !name || !licenses || !authors
76 fail "#{name || dir} required to set name, license(s) and author(s)"
79 build.libmruby << @objs
81 instance_eval(&@build_config_initializer) if @build_config_initializer
83 compilers.each do |compiler|
84 compiler.define_rules build_dir, "#{dir}/"
87 define_gem_init_builder
90 def add_dependency(name, *requirements)
91 requirements = ['>= 0.0.0'] if requirements.empty?
93 @dependencies << {:gem => name, :requirements => requirements}
101 "#{build.build_dir}/mrbgems/#{name}"
105 "#{build_dir}/gem_test.c"
109 @funcname ||= @name.gsub('-', '_')
113 MRuby::Build::COMPILERS.map do |c|
114 instance_variable_get("@#{c}")
118 def define_gem_init_builder
119 file objfile("#{build_dir}/gem_init") => "#{build_dir}/gem_init.c"
120 file "#{build_dir}/gem_init.c" => [build.mrbcfile] + [rbfiles].flatten do |t|
121 FileUtils.mkdir_p build_dir
122 generate_gem_init("#{build_dir}/gem_init.c")
126 def generate_gem_init(fname)
127 open(fname, 'w') do |f|
128 print_gem_init_header f
129 build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}" unless rbfiles.empty?
130 f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);]
131 f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);]
133 f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {]
134 f.puts %Q[ int ai = mrb_gc_arena_save(mrb);]
135 f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
136 unless rbfiles.empty?
137 f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
138 f.puts %Q[ if (mrb->exc) {]
139 f.puts %Q[ mrb_print_backtrace(mrb);]
140 f.puts %Q[ mrb_p(mrb, mrb_obj_value(mrb->exc));]
141 f.puts %Q[ exit(EXIT_FAILURE);]
144 f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
147 f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {]
148 f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
151 end # generate_gem_init
153 def print_gem_init_header(f)
155 f.puts %Q[ * This file is loading the irep]
156 f.puts %Q[ * Ruby GEM code.]
158 f.puts %Q[ * IMPORTANT:]
159 f.puts %Q[ * This file was generated!]
160 f.puts %Q[ * All manual changes will get lost.]
162 f.puts %Q[#include <stdlib.h>]
163 f.puts %Q[#include "mruby.h"]
164 f.puts %Q[#include "mruby/irep.h"]
165 f.puts %Q[#include "mruby/dump.h"]
166 f.puts %Q[#include "mruby/string.h"]
167 f.puts %Q[#include "mruby/proc.h"]
168 f.puts %Q[#include "mruby/variable.h"]
169 f.puts %Q[#include "mruby/array.h"]
170 f.puts %Q[#include "mruby/hash.h"]
173 def version_ok?(req_versions)
174 req_versions.map do |req|
176 cmp_result = Version.new(version) <=> Version.new(ver)
178 when '=' then cmp_result == 0
179 when '!=' then cmp_result != 0
180 when '>' then cmp_result == 1
181 when '<' then cmp_result == -1
182 when '>=' then cmp_result >= 0
183 when '<=' then cmp_result <= 0
185 Version.new(version).twiddle_wakka_ok?(Version.new(ver))
187 fail "Comparison not possible with '#{cmp}'"
203 ret = own.next <=> oth
208 break unless ret == 0
214 # ~> compare algorithm
217 # ~> 2.2 means >= 2.2.0 and < 3.0.0
218 # ~> 2.2.0 means >= 2.2.0 and < 2.3.0
219 def twiddle_wakka_ok?(other)
220 gr_or_eql = (self <=> other) >= 0
221 still_minor = (self <=> other.skip_minor) < 0
222 gr_or_eql and still_minor
234 @ary = @str.split('.').map(&:to_i)
237 def each(&block); @ary.each(&block); end
238 def [](index); @ary[index]; end
239 def []=(index, value)
241 @str = @ary.join('.')
245 @str = @ary.join('.')
261 unless @ary.detect {|g| g.dir == gem.dir }
264 # GEM was already added to this list
274 g.dependencies.each do |dep|
276 req_versions = dep[:requirements]
278 # check each GEM dependency against all available GEMs
280 if name == dep_g.name
281 unless dep_g.version_ok?(req_versions)
282 fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'"
294 def new(&block); block.call(self); end
295 def config=(obj); @config = obj; end
296 def gem(gemdir, &block); @config.gem(gemdir, &block); end