mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	 3f059374b8
			
		
	
	
		3f059374b8
		
	
	
	
	
		
			
			This makes auditing for inadvertant command execution easier. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61790 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			243 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| #
 | |
| # iseq_loader.rb - sample of compiler/loader for binary compiled file
 | |
| #
 | |
| # Usage as a compiler: ruby iseq_loader.rb [file or directory] ...
 | |
| #
 | |
| #   It compiles and stores specified files.
 | |
| #   If directories are specified, then compiles and stores all *.rb files.
 | |
| #   (using Dir.glob)
 | |
| #
 | |
| # TODO: add remove option
 | |
| # TODO: add verify option
 | |
| #
 | |
| # Usage as a loader: simply require this file with the following setting.
 | |
| #
 | |
| # Setting with environment variables.
 | |
| #
 | |
| #  * RUBY_ISEQ_LOADER_STORAGE to select storage type
 | |
| #    * dbm: use dbm
 | |
| #    * fs: [default] use file system. locate a compiled binary files in same
 | |
| #      directory of scripts like Rubinius. foo.rb.yarb will be created for foo.rb.
 | |
| #    * fs2: use file system. locate compiled file in specified directory.
 | |
| #    * nothing: do nothing.
 | |
| #
 | |
| #  * RUBY_ISEQ_LOADER_STORAGE_DIR to select directory
 | |
| #    * default: ~/.ruby_binaries/
 | |
| #
 | |
| #  * RUBY_ISEQ_LOADER_STORAGE_COMPILE_IF_NOT_COMPILED
 | |
| #    * true: store compiled file if compiled data is not available.
 | |
| #    * false: [default] do nothing if there is no compiled iseq data.
 | |
| 
 | |
| class RubyVM::InstructionSequence
 | |
|   $ISEQ_LOADER_LOADED = 0
 | |
|   $ISEQ_LOADER_COMPILED = 0
 | |
|   $ISEQ_LOADER_IGNORED = 0
 | |
|   LAUNCHED_TIME = Time.now
 | |
|   COMPILE_FILE_ENABLE = false || true
 | |
|   COMPILE_VERBOSE = $VERBOSE || false # || true
 | |
|   COMPILE_DEBUG = ENV['RUBY_ISEQ_LOADER_DEBUG']
 | |
|   COMPILE_IF_NOT_COMPILED = ENV['RUBY_ISEQ_LOADER_STORAGE_COMPILE_IF_NOT_COMPILED'] == 'true'
 | |
| 
 | |
|   at_exit{
 | |
|     STDERR.puts "[ISEQ_LOADER] #{Process.pid} time: #{Time.now - LAUNCHED_TIME}, " +
 | |
|                 "loaded: #{$ISEQ_LOADER_LOADED}, " +
 | |
|                 "compiled: #{$ISEQ_LOADER_COMPILED}, " +
 | |
|                 "ignored: #{$ISEQ_LOADER_IGNORED}"
 | |
|   } if COMPILE_VERBOSE
 | |
| 
 | |
|   unless cf_dir = ENV['RUBY_ISEQ_LOADER_STORAGE_DIR']
 | |
|     cf_dir = File.expand_path("~/.ruby_binaries")
 | |
|     unless File.exist?(cf_dir)
 | |
|       Dir.mkdir(cf_dir)
 | |
|     end
 | |
|   end
 | |
|   CF_PREFIX = "#{cf_dir}/cb."
 | |
| 
 | |
|   class NullStorage
 | |
|     def load_iseq fname; end
 | |
|     def compile_and_save_isq fname; end
 | |
|     def unlink_compiled_iseq; end
 | |
|   end
 | |
| 
 | |
|   class BasicStorage
 | |
|     def initialize
 | |
|       require 'digest/sha1'
 | |
|     end
 | |
| 
 | |
|     def load_iseq fname
 | |
|       iseq_key = iseq_key_name(fname)
 | |
|       if compiled_iseq_exist?(fname, iseq_key) && compiled_iseq_is_younger?(fname, iseq_key)
 | |
|         $ISEQ_LOADER_LOADED += 1
 | |
|         STDERR.puts "[ISEQ_LOADER] #{Process.pid} load #{fname} from #{iseq_key}" if COMPILE_DEBUG
 | |
|         binary = read_compiled_iseq(fname, iseq_key)
 | |
|         iseq = RubyVM::InstructionSequence.load_from_binary(binary)
 | |
|         # p [extra_data(iseq.path), RubyVM::InstructionSequence.load_from_binary_extra_data(binary)]
 | |
|         # raise unless extra_data(iseq.path) == RubyVM::InstructionSequence.load_from_binary_extra_data(binary)
 | |
|         iseq
 | |
|       elsif COMPILE_IF_NOT_COMPILED
 | |
|         compile_and_save_iseq(fname, iseq_key)
 | |
|       else
 | |
|         $ISEQ_LOADER_IGNORED += 1
 | |
|         # p fname
 | |
|         nil
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def extra_data fname
 | |
|       "SHA-1:#{::Digest::SHA1.file(fname).digest}"
 | |
|     end
 | |
| 
 | |
|     def compile_and_save_iseq fname, iseq_key = iseq_key_name(fname)
 | |
|       $ISEQ_LOADER_COMPILED += 1
 | |
|       STDERR.puts "[RUBY_COMPILED_FILE] compile #{fname}" if COMPILE_DEBUG
 | |
|       iseq = RubyVM::InstructionSequence.compile_file(fname)
 | |
| 
 | |
|       binary = iseq.to_binary(extra_data(fname))
 | |
|       write_compiled_iseq(fname, iseq_key, binary)
 | |
|       iseq
 | |
|     end
 | |
| 
 | |
|     # def unlink_compiled_iseq; nil; end # should implement at sub classes
 | |
| 
 | |
|     private
 | |
| 
 | |
|     def iseq_key_name fname
 | |
|       fname
 | |
|     end
 | |
| 
 | |
|     # should implement at sub classes
 | |
|     # def compiled_iseq_younger? fname, iseq_key; end
 | |
|     # def compiled_iseq_exist? fname, iseq_key; end
 | |
|     # def read_compiled_file fname, iseq_key; end
 | |
|     # def write_compiled_file fname, iseq_key, binary; end
 | |
|   end
 | |
| 
 | |
|   class FSStorage < BasicStorage
 | |
|     def initialize
 | |
|       super
 | |
|       require 'fileutils'
 | |
|       @dir = CF_PREFIX + "files"
 | |
|       unless File.directory?(@dir)
 | |
|         FileUtils.mkdir_p(@dir)
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def unlink_compiled_iseq
 | |
|       File.unlink(compile_file_path)
 | |
|     end
 | |
| 
 | |
|     private
 | |
| 
 | |
|     def iseq_key_name fname
 | |
|       "#{fname}.yarb" # same directory
 | |
|     end
 | |
| 
 | |
|     def compiled_iseq_exist? fname, iseq_key
 | |
|       File.exist?(iseq_key)
 | |
|     end
 | |
| 
 | |
|     def compiled_iseq_is_younger? fname, iseq_key
 | |
|       File.mtime(iseq_key) >= File.mtime(fname)
 | |
|     end
 | |
| 
 | |
|     def read_compiled_iseq fname, iseq_key
 | |
|       File.open(iseq_key, 'rb'){|f| f.read}
 | |
|     end
 | |
| 
 | |
|     def write_compiled_iseq fname, iseq_key, binary
 | |
|       File.open(iseq_key, 'wb'){|f| f.write(binary)}
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   class FS2Storage < FSStorage
 | |
|     def iseq_key_name fname
 | |
|       @dir + fname.gsub(/[^A-Za-z0-9\._-]/){|c| '%02x' % c.ord} # special directory
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   class DBMStorage < BasicStorage
 | |
|     def initialize
 | |
|       require 'dbm'
 | |
|       @db = DBM.open(CF_PREFIX+'db')
 | |
|     end
 | |
| 
 | |
|     def unlink_compiled_iseq
 | |
|       @db.delete fname
 | |
|     end
 | |
| 
 | |
|     private
 | |
| 
 | |
|     def date_key_name fname
 | |
|       "date.#{fname}"
 | |
|     end
 | |
| 
 | |
|     def iseq_key_name fname
 | |
|       "body.#{fname}"
 | |
|     end
 | |
| 
 | |
|     def compiled_iseq_exist? fname, iseq_key
 | |
|       @db.has_key? iseq_key
 | |
|     end
 | |
| 
 | |
|     def compiled_iseq_is_younger? fname, iseq_key
 | |
|       date_key = date_key_name(fname)
 | |
|       if @db.has_key? date_key
 | |
|         @db[date_key].to_i >= File.mtime(fname).to_i
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     def read_compiled_iseq fname, iseq_key
 | |
|       @db[iseq_key]
 | |
|     end
 | |
| 
 | |
|     def write_compiled_iseq fname, iseq_key, binary
 | |
|       date_key = date_key_name(fname)
 | |
|       @db[iseq_key] = binary
 | |
|       @db[date_key] = Time.now.to_i
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   STORAGE = case ENV['RUBY_ISEQ_LOADER_STORAGE']
 | |
|             when 'dbm'
 | |
|               DBMStorage.new
 | |
|             when 'fs'
 | |
|               FSStorage.new
 | |
|             when 'fs2'
 | |
|               FS2Storage.new
 | |
|             when 'null'
 | |
|               NullStorage.new
 | |
|             else
 | |
|               FSStorage.new
 | |
|             end
 | |
| 
 | |
|   STDERR.puts "[ISEQ_LOADER] use #{STORAGE.class} " if COMPILE_VERBOSE
 | |
| 
 | |
|   def self.load_iseq fname
 | |
|     STORAGE.load_iseq(fname)
 | |
|   end
 | |
| 
 | |
|   def self.compile_and_save_iseq fname
 | |
|     STORAGE.compile_and_save_iseq fname
 | |
|   end
 | |
| 
 | |
|   def self.unlink_compiled_iseq fname
 | |
|     STORAGE.unlink_compiled_iseq fname
 | |
|   end
 | |
| end
 | |
| 
 | |
| if __FILE__ == $0
 | |
|   ARGV.each{|path|
 | |
|     if File.directory?(path)
 | |
|       pattern = File.join(path, '**/*.rb')
 | |
|       Dir.glob(pattern){|file|
 | |
|         begin
 | |
|           RubyVM::InstructionSequence.compile_and_save_iseq(file)
 | |
|         rescue SyntaxError => e
 | |
|           STDERR.puts e
 | |
|         end
 | |
|       }
 | |
|     else
 | |
|       RubyVM::InstructionSequence.compile_and_save_iseq(path)
 | |
|     end
 | |
|   }
 | |
| end
 |