require 'tempfile' module FFI # @private class TypesGenerator ## # Maps different C types to the C type representations we use TYPE_MAP = { "char" => :char, "signed char" => :char, "__signed char" => :char, "unsigned char" => :uchar, "short" => :short, "signed short" => :short, "signed short int" => :short, "unsigned short" => :ushort, "unsigned short int" => :ushort, "int" => :int, "signed int" => :int, "unsigned int" => :uint, "long" => :long, "long int" => :long, "signed long" => :long, "signed long int" => :long, "unsigned long" => :ulong, "unsigned long int" => :ulong, "long unsigned int" => :ulong, "long long" => :long_long, "long long int" => :long_long, "signed long long" => :long_long, "signed long long int" => :long_long, "unsigned long long" => :ulong_long, "unsigned long long int" => :ulong_long, "char *" => :string, "void *" => :pointer, } def self.generate(options = {}) typedefs = nil Tempfile.open 'ffi_types_generator' do |io| io.puts <<-C #include #if !(defined(WIN32)) #include #include #endif C io.close cc = ENV['CC'] || 'gcc' cmd = "#{cc} -E -x c #{options[:cppflags]} -D_DARWIN_USE_64_BIT_INODE -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -c" if options[:input] typedefs = File.read(options[:input]) elsif options[:remote] typedefs = `ssh #{options[:remote]} #{cmd} - < #{io.path}` else typedefs = `#{cmd} #{io.path}` end end code = "" typedefs.each_line do |type| # We only care about single line typedef next unless type =~ /typedef/ # Ignore unions or structs next if type =~ /union|struct/ # strip off the starting typedef and ending ; type.gsub!(/^(.*typedef\s*)/, "") type.gsub!(/\s*;\s*$/, "") parts = type.split(/\s+/) def_type = parts.join(" ") # GCC does mapping with __attribute__ stuf, also see # http://hal.cs.berkeley.edu/cil/cil016.html section 16.2.7. Problem # with this is that the __attribute__ stuff can either occur before or # after the new type that is defined... if type =~ /__attribute__/ if parts.last =~ /__QI__|__HI__|__SI__|__DI__|__word__/ # In this case, the new type is BEFORE __attribute__ we need to # find the final_type as the type before the part that starts with # __attribute__ final_type = "" parts.each do |p| break if p =~ /__attribute__/ final_type = p end else final_type = parts.pop end def_type = case type when /__QI__/ then "char" when /__HI__/ then "short" when /__SI__/ then "int" when /__DI__/ then "long long" when /__word__/ then "long" else "int" end def_type = "unsigned #{def_type}" if type =~ /unsigned/ else final_type = parts.pop def_type = parts.join(" ") end if type = TYPE_MAP[def_type] code << "rbx.platform.typedef.#{final_type} = #{type}\n" TYPE_MAP[final_type] = TYPE_MAP[def_type] else # Fallback to an ordinary pointer if we don't know the type if def_type =~ /\*/ code << "rbx.platform.typedef.#{final_type} = pointer\n" TYPE_MAP[final_type] = :pointer end end end code end end end