source: EcnlProtoTool/trunk/mruby-1.3.0/tasks/mrbgem_spec.rake@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-ruby;charset=UTF-8
File size: 13.5 KB
Line 
1require 'pathname'
2require 'forwardable'
3require 'tsort'
4require 'shellwords'
5
6module MRuby
7 module Gem
8 class << self
9 attr_accessor :current
10 end
11 LinkerConfig = Struct.new(:libraries, :library_paths, :flags, :flags_before_libraries, :flags_after_libraries)
12
13 class Specification
14 include Rake::DSL
15 extend Forwardable
16 def_delegators :@build, :filename, :objfile, :libfile, :exefile
17
18 attr_accessor :name, :dir, :build
19 alias mruby build
20 attr_accessor :build_config_initializer
21 attr_accessor :mrblib_dir, :objs_dir
22
23 attr_accessor :version
24 attr_accessor :description, :summary
25 attr_accessor :homepage
26 attr_accessor :licenses, :authors
27 alias :license= :licenses=
28 alias :author= :authors=
29
30 attr_accessor :rbfiles, :objs
31 attr_accessor :test_objs, :test_rbfiles, :test_args
32 attr_accessor :test_preload
33
34 attr_accessor :bins
35
36 attr_accessor :requirements
37 attr_reader :dependencies, :conflicts
38
39 attr_accessor :export_include_paths
40
41 attr_reader :generate_functions
42
43 attr_block MRuby::Build::COMMANDS
44
45 def initialize(name, &block)
46 @name = name
47 @initializer = block
48 @version = "0.0.0"
49 @mrblib_dir = "mrblib"
50 @objs_dir = "src"
51 MRuby::Gem.current = self
52 end
53
54 def setup
55 MRuby::Gem.current = self
56 MRuby::Build::COMMANDS.each do |command|
57 instance_variable_set("@#{command}", @build.send(command).clone)
58 end
59 @linker = LinkerConfig.new([], [], [], [], [])
60
61 @rbfiles = Dir.glob("#{@dir}/#{@mrblib_dir}/**/*.rb").sort
62 @objs = Dir.glob("#{@dir}/#{@objs_dir}/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
63 objfile(f.relative_path_from(@dir).to_s.pathmap("#{build_dir}/%X"))
64 end
65
66 @generate_functions = !(@rbfiles.empty? && @objs.empty?)
67 @objs << objfile("#{build_dir}/gem_init") if @generate_functions
68
69 @test_rbfiles = Dir.glob("#{dir}/test/**/*.rb")
70 @test_objs = Dir.glob("#{dir}/test/*.{c,cpp,cxx,cc,m,asm,s,S}").map do |f|
71 objfile(f.relative_path_from(dir).to_s.pathmap("#{build_dir}/%X"))
72 end
73 @custom_test_init = !@test_objs.empty?
74 @test_preload = nil # 'test/assert.rb'
75 @test_args = {}
76
77 @bins = []
78
79 @requirements = []
80 @dependencies, @conflicts = [], []
81 @export_include_paths = []
82 @export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
83
84 instance_eval(&@initializer)
85
86 if !name || !licenses || !authors
87 fail "#{name || dir} required to set name, license(s) and author(s)"
88 end
89
90 build.libmruby << @objs
91
92 instance_eval(&@build_config_initializer) if @build_config_initializer
93 end
94
95 def setup_compilers
96 compilers.each do |compiler|
97 compiler.define_rules build_dir, "#{dir}"
98 compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}]
99 compiler.include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
100 end
101
102 define_gem_init_builder if @generate_functions
103 end
104
105 def add_dependency(name, *requirements)
106 default_gem = requirements.last.kind_of?(Hash) ? requirements.pop : nil
107 requirements = ['>= 0.0.0'] if requirements.empty?
108 requirements.flatten!
109 @dependencies << {:gem => name, :requirements => requirements, :default => default_gem}
110 end
111
112 def add_test_dependency(*args)
113 add_dependency(*args) if build.test_enabled?
114 end
115
116 def add_conflict(name, *req)
117 @conflicts << {:gem => name, :requirements => req.empty? ? nil : req}
118 end
119
120 def self.bin=(bin)
121 @bins = [bin].flatten
122 end
123
124 def build_dir
125 "#{build.build_dir}/mrbgems/#{name}"
126 end
127
128 def test_rbireps
129 "#{build_dir}/gem_test.c"
130 end
131
132 def search_package(name, version_query=nil)
133 package_query = name
134 package_query += " #{version_query}" if version_query
135 _pp "PKG-CONFIG", package_query
136 escaped_package_query = Shellwords.escape(package_query)
137 if system("pkg-config --exists #{escaped_package_query}")
138 cc.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
139 cxx.flags += [`pkg-config --cflags #{escaped_package_query}`.strip]
140 linker.flags_before_libraries += [`pkg-config --libs #{escaped_package_query}`.strip]
141 true
142 else
143 false
144 end
145 end
146
147 def funcname
148 @funcname ||= @name.gsub('-', '_')
149 end
150
151 def compilers
152 MRuby::Build::COMPILERS.map do |c|
153 instance_variable_get("@#{c}")
154 end
155 end
156
157 def define_gem_init_builder
158 file objfile("#{build_dir}/gem_init") => [ "#{build_dir}/gem_init.c", File.join(dir, "mrbgem.rake") ]
159 file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t|
160 FileUtils.mkdir_p build_dir
161 generate_gem_init("#{build_dir}/gem_init.c")
162 end
163 end
164
165 def generate_gem_init(fname)
166 open(fname, 'w') do |f|
167 print_gem_init_header f
168 build.mrbc.run f, rbfiles, "gem_mrblib_irep_#{funcname}" unless rbfiles.empty?
169 f.puts %Q[void mrb_#{funcname}_gem_init(mrb_state *mrb);]
170 f.puts %Q[void mrb_#{funcname}_gem_final(mrb_state *mrb);]
171 f.puts %Q[]
172 f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_init(mrb_state *mrb) {]
173 f.puts %Q[ int ai = mrb_gc_arena_save(mrb);]
174 f.puts %Q[ mrb_#{funcname}_gem_init(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
175 unless rbfiles.empty?
176 f.puts %Q[ mrb_load_irep(mrb, gem_mrblib_irep_#{funcname});]
177 f.puts %Q[ if (mrb->exc) {]
178 f.puts %Q[ mrb_print_error(mrb);]
179 f.puts %Q[ exit(EXIT_FAILURE);]
180 f.puts %Q[ }]
181 end
182 f.puts %Q[ mrb_gc_arena_restore(mrb, ai);]
183 f.puts %Q[}]
184 f.puts %Q[]
185 f.puts %Q[void GENERATED_TMP_mrb_#{funcname}_gem_final(mrb_state *mrb) {]
186 f.puts %Q[ mrb_#{funcname}_gem_final(mrb);] if objs != [objfile("#{build_dir}/gem_init")]
187 f.puts %Q[}]
188 end
189 end # generate_gem_init
190
191 def print_gem_comment(f)
192 f.puts %Q[/*]
193 f.puts %Q[ * This file is loading the irep]
194 f.puts %Q[ * Ruby GEM code.]
195 f.puts %Q[ *]
196 f.puts %Q[ * IMPORTANT:]
197 f.puts %Q[ * This file was generated!]
198 f.puts %Q[ * All manual changes will get lost.]
199 f.puts %Q[ */]
200 end
201
202 def print_gem_init_header(f)
203 print_gem_comment(f)
204 f.puts %Q[#include <stdlib.h>] unless rbfiles.empty?
205 f.puts %Q[#include <mruby.h>]
206 f.puts %Q[#include <mruby/irep.h>] unless rbfiles.empty?
207 end
208
209 def print_gem_test_header(f)
210 print_gem_comment(f)
211 f.puts %Q[#include <stdio.h>]
212 f.puts %Q[#include <stdlib.h>]
213 f.puts %Q[#include <mruby.h>]
214 f.puts %Q[#include <mruby/irep.h>]
215 f.puts %Q[#include <mruby/variable.h>]
216 f.puts %Q[#include <mruby/hash.h>] unless test_args.empty?
217 end
218
219 def test_dependencies
220 [@name]
221 end
222
223 def custom_test_init?
224 @custom_test_init
225 end
226
227 def version_ok?(req_versions)
228 req_versions.map do |req|
229 cmp, ver = req.split
230 cmp_result = Version.new(version) <=> Version.new(ver)
231 case cmp
232 when '=' then cmp_result == 0
233 when '!=' then cmp_result != 0
234 when '>' then cmp_result == 1
235 when '<' then cmp_result == -1
236 when '>=' then cmp_result >= 0
237 when '<=' then cmp_result <= 0
238 when '~>'
239 Version.new(version).twiddle_wakka_ok?(Version.new(ver))
240 else
241 fail "Comparison not possible with '#{cmp}'"
242 end
243 end.all?
244 end
245 end # Specification
246
247 class Version
248 include Comparable
249 include Enumerable
250
251 def <=>(other)
252 ret = 0
253 own = to_enum
254
255 other.each do |oth|
256 begin
257 ret = own.next <=> oth
258 rescue StopIteration
259 ret = 0 <=> oth
260 end
261
262 break unless ret == 0
263 end
264
265 ret
266 end
267
268 # ~> compare algorithm
269 #
270 # Example:
271 # ~> 2.2 means >= 2.2.0 and < 3.0.0
272 # ~> 2.2.0 means >= 2.2.0 and < 2.3.0
273 def twiddle_wakka_ok?(other)
274 gr_or_eql = (self <=> other) >= 0
275 still_minor = (self <=> other.skip_minor) < 0
276 gr_or_eql and still_minor
277 end
278
279 def skip_minor
280 a = @ary.dup
281 a.slice!(-1)
282 a[-1] = a[-1].succ
283 a
284 end
285
286 def initialize(str)
287 @str = str
288 @ary = @str.split('.').map(&:to_i)
289 end
290
291 def each(&block); @ary.each(&block); end
292 def [](index); @ary[index]; end
293 def []=(index, value)
294 @ary[index] = value
295 @str = @ary.join('.')
296 end
297 def slice!(index)
298 @ary.slice!(index)
299 @str = @ary.join('.')
300 end
301 end # Version
302
303 class List
304 include Enumerable
305
306 def initialize
307 @ary = []
308 end
309
310 def each(&b)
311 @ary.each(&b)
312 end
313
314 def <<(gem)
315 unless @ary.detect {|g| g.dir == gem.dir }
316 @ary << gem
317 else
318 # GEM was already added to this list
319 end
320 end
321
322 def empty?
323 @ary.empty?
324 end
325
326 def generate_gem_table build
327 gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
328
329 default_gems = []
330 each do |g|
331 g.dependencies.each do |dep|
332 unless gem_table.key? dep[:gem]
333 if dep[:default]; default_gems << dep
334 elsif File.exist? "#{MRUBY_ROOT}/mrbgems/#{dep[:gem]}" # check core
335 default_gems << { :gem => dep[:gem], :default => { :core => dep[:gem] } }
336 else # fallback to mgem-list
337 default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
338 end
339 end
340 end
341 end
342
343 until default_gems.empty?
344 def_gem = default_gems.pop
345
346 spec = build.gem def_gem[:default]
347 fail "Invalid gem name: #{spec.name} (Expected: #{def_gem[:gem]})" if spec.name != def_gem[:gem]
348 spec.setup
349
350 spec.dependencies.each do |dep|
351 unless gem_table.key? dep[:gem]
352 if dep[:default]; default_gems << dep
353 else default_gems << { :gem => dep[:gem], :default => { :mgem => dep[:gem] } }
354 end
355 end
356 end
357 gem_table[spec.name] = spec
358 end
359
360 each do |g|
361 g.dependencies.each do |dep|
362 name = dep[:gem]
363 req_versions = dep[:requirements]
364 dep_g = gem_table[name]
365
366 # check each GEM dependency against all available GEMs
367 if dep_g.nil?
368 fail "The GEM '#{g.name}' depends on the GEM '#{name}' but it could not be found"
369 end
370 unless dep_g.version_ok? req_versions
371 fail "#{name} version should be #{req_versions.join(' and ')} but was '#{dep_g.version}'"
372 end
373 end
374
375 cfls = g.conflicts.select { |c|
376 cfl_g = gem_table[c[:gem]]
377 cfl_g and cfl_g.version_ok?(c[:requirements] || ['>= 0.0.0'])
378 }.map { |c| "#{c[:gem]}(#{gem_table[c[:gem]].version})" }
379 fail "Conflicts of gem `#{g.name}` found: #{cfls.join ', '}" unless cfls.empty?
380 end
381
382 gem_table
383 end
384
385 def tsort_dependencies ary, table, all_dependency_listed = false
386 unless all_dependency_listed
387 left = ary.dup
388 until left.empty?
389 v = left.pop
390 table[v].dependencies.each do |dep|
391 left.push dep[:gem]
392 ary.push dep[:gem]
393 end
394 end
395 end
396
397 ary.uniq!
398 table.instance_variable_set :@root_gems, ary
399 class << table
400 include TSort
401 def tsort_each_node &b
402 @root_gems.each &b
403 end
404
405 def tsort_each_child(n, &b)
406 fetch(n).dependencies.each do |v|
407 b.call v[:gem]
408 end
409 end
410 end
411
412 begin
413 table.tsort.map { |v| table[v] }
414 rescue TSort::Cyclic => e
415 fail "Circular mrbgem dependency found: #{e.message}"
416 end
417 end
418
419 def check(build)
420 gem_table = generate_gem_table build
421
422 @ary = tsort_dependencies gem_table.keys, gem_table, true
423
424 each(&:setup_compilers)
425
426 each do |g|
427 import_include_paths(g)
428 end
429 end
430
431 def import_include_paths(g)
432 gem_table = @ary.reduce({}) { |res,v| res[v.name] = v; res }
433 g.dependencies.each do |dep|
434 dep_g = gem_table[dep[:gem]]
435 # We can do recursive call safely
436 # as circular dependency has already detected in the caller.
437 import_include_paths(dep_g)
438
439 dep_g.export_include_paths.uniq!
440 g.compilers.each do |compiler|
441 compiler.include_paths += dep_g.export_include_paths
442 g.export_include_paths += dep_g.export_include_paths
443 compiler.include_paths.uniq!
444 g.export_include_paths.uniq!
445 end
446 end
447 end
448 end # List
449 end # Gem
450
451 GemBox = Object.new
452 class << GemBox
453 attr_accessor :path
454
455 def new(&block); block.call(self); end
456 def config=(obj); @config = obj; end
457 def gem(gemdir, &block); @config.gem(gemdir, &block); end
458 end # GemBox
459end # MRuby
Note: See TracBrowser for help on using the repository browser.