inline-require 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/env ruby
  2. ###
  3. ### inline-require - expand 'require "foo"' into inline code
  4. ###
  5. ### usage: inline-require [-h] [-I path[,path2,..]] script
  6. ###
  7. ### copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
  8. ### 2.7.0
  9. ### $Rev: 10 $
  10. ###
  11. class InlineRequire
  12. def initialize(opts={})
  13. @opts = opts.dup
  14. end
  15. attr_accessor :opts
  16. def expand(filename)
  17. sbuf = ''
  18. inlined = []
  19. level = 0
  20. expand_require(filename, sbuf, inlined, level)
  21. return sbuf
  22. end
  23. private
  24. def expand_require(filename, sbuf, inlined, level)
  25. raise "*** assertion error" if inlined.include?(filename)
  26. remove_comment = @opts[:remove_comment]
  27. expand_indented = @opts[:expand_indented]
  28. keep_filename = @opts[:keep_filename]
  29. loaded_features = @opts[:loaded_features]
  30. inlined << filename
  31. prog = File.read(filename)
  32. n = 0
  33. flag_if_file = false
  34. prog.each_line do |line|
  35. n += 1
  36. ## comment out from 'if __FILE__ == $0' to 'end'
  37. if level > 0
  38. if flag_if_file
  39. sbuf << "#" << line
  40. flag_if_file = false if line =~ /^end$/
  41. next
  42. end
  43. if line =~ /^if\s+__FILE__\s*==\s*\$0(\s+then)?$/ || line =~ /^if\s+\$0\s*==\s*__FILE__(\s+then)?$/
  44. flag_if_file = true
  45. sbuf << "#" << line
  46. next
  47. end
  48. end
  49. ## find 'require "foo"' and expand it to inline code
  50. flag_inline = false
  51. pattern = expand_indented ? /^[ \t]*require ['"](.*)["']\s*$/ \
  52. : /^require ['"](.*)["']\s*$/
  53. if line =~ pattern
  54. libname = $1
  55. libpath = find_library(libname)
  56. $stderr.puts "*** debug: libpath=#{libpath.inspect}" if $debug_mode
  57. unless libpath
  58. #raise "file '#{filename}'(line #{n}): library '#{libname}' not found."
  59. warn "file '#{filename}'(line #{n}): library '#{libname}' not found."
  60. else
  61. flag_inline = true if libpath =~ /\.rb$/ && local_library?(libpath)
  62. end
  63. end
  64. if !flag_inline
  65. sbuf << line unless remove_comment && line =~ /^[ \t]*\#/
  66. elsif inlined.include?(libpath)
  67. sbuf << "#--already included #{line}" unless remove_comment
  68. else
  69. if keep_filename
  70. @n ||= 0; @n += 1; n = @n
  71. end
  72. sbuf << "#--begin of #{line}" unless remove_comment
  73. sbuf << "$LOADED_FEATURES << '#{libname}.rb'\n" if loaded_features
  74. sbuf << "eval <<'END_OF_SCRIPT__#{n}', TOPLEVEL_BINDING, '#{libpath}', 1\n" if keep_filename
  75. expand_require(libpath, sbuf, inlined, level+1)
  76. sbuf << "END_OF_SCRIPT__#{n}\n" if keep_filename
  77. sbuf << "#--end of #{line}" unless remove_comment
  78. end
  79. end
  80. #sbuf << "\n" if sbuf[-1] != ?\n
  81. end
  82. def local_library?(libpath)
  83. return libpath !~ /^\//
  84. end
  85. def find_library(libname)
  86. if libname =~ /^\.rb$/
  87. libname_rb = libname
  88. libname_so = nil
  89. elsif libname =~ /^\.so$/
  90. libname_rb = nil
  91. libname_so = libname
  92. else
  93. libname_rb = libname + ".rb"
  94. libname_so = libname + ".so"
  95. end
  96. $LOAD_PATH.each do |path|
  97. if libname_rb
  98. libpath = path + "/" + libname_rb
  99. return libpath if test(?f, libpath)
  100. end
  101. if libname_so
  102. libpath = path + "/" + libname_so
  103. return libpath if test(?f, libpath)
  104. end
  105. end
  106. return nil
  107. end
  108. end
  109. if __FILE__ == $0
  110. begin
  111. require "optparse"
  112. op = OptionParser.new
  113. options = {}
  114. op.on("-h", "--help") {|v| options[:help] = v }
  115. op.on("-I libpath") {|v| options[:libpath] = v }
  116. op.on("-i") {|v| options[:expand_indented] = v }
  117. op.on("-c") {|v| options[:remove_comment] = v }
  118. op.on("-k") {|v| options[:keep_filename] = v }
  119. op.on("-l") {|v| options[:loaded_features] = v }
  120. op.on("-D") {|v| options[:debug] = v }
  121. op.parse!()
  122. $debug_mode = options[:debug]
  123. if options[:help]
  124. command = File.basename($0)
  125. puts "Usage: #{command} [-h] [-I path[,path2,..]] script"
  126. puts " -h : help"
  127. puts " -i : expand indented require(), too"
  128. puts " -c : remove comment lines start with '#'"
  129. puts " -k : keep filename (for debugging)"
  130. puts " -l : append libs to $LOADED_FEATURES"
  131. puts " -I path[,path2,...] : ruby library path"
  132. exit(0)
  133. end
  134. if options[:libpath]
  135. rubylib_paths = options[:libpath].split(/,/)
  136. else
  137. rubylib_paths = []
  138. end
  139. $stderr.puts "*** debug: rubylib_paths=#{rubylib_paths.inspect}" if $debug_mode
  140. $LOAD_PATH.concat(rubylib_paths)
  141. filenames = ARGV
  142. opts = { :expand_indented => options[:expand_indented],
  143. :remove_comment => options[:remove_comment],
  144. :keep_filename => options[:keep_filename],
  145. :loaded_features => options[:loaded_features] }
  146. inline_require = InlineRequire.new(opts)
  147. filenames.each do |filename|
  148. print inline_require.expand(filename)
  149. end
  150. rescue => ex
  151. if $debug_mode
  152. raise ex
  153. else
  154. $stderr.puts ex.message
  155. end
  156. end
  157. end