# coding: ascii require 'mkmf' # compatibility for ruby-1.9 RbConfig = Config unless defined? RbConfig if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # Windows require 'win32/registry' module Registry class OracleHome attr_reader :name attr_reader :path def initialize(name, path) @name = name @path = path end end def self.oracle_homes homes = [] Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Oracle') do |key| key.each_key do |subkey_name| subkey = key.open(subkey_name) begin homes << OracleHome.new(subkey['ORACLE_HOME_NAME'], subkey['ORACLE_HOME'].chomp('\\')) rescue Win32::Registry::Error # ignore errors end end end homes end end end # minimal implementation to read information of a shared object. class MiniSOReader attr_reader :file_format attr_reader :cpu attr_reader :endian attr_reader :bits def initialize(filename) f = open(filename, 'rb') begin case file_header = f.read(2) when "\177E" # Linux, Solaris and HP-UX(64-bit) read_elf(f) if f.read(2) == 'LF' when "MZ" # Windows read_pe(f) when "\x02\x10" # HP-UX PA-RISC1.1 read_parisc(f) when "\xfe\xed" # Big-endian Mach-O File read_mach_o_be(f) when "\xce\xfa" # 32-bit Little-endian Mach-O File read_mach_o_le(f, 32) when "\xcf\xfa" # 64-bit Little-endian Mach-O File read_mach_o_le(f, 64) when "\xca\xfe" # Universal binary read_mach_o_unversal(f) else # AIX and Tru64 raise format("unknown file header: %02x %02x", file_header[0].to_i, file_header[1].to_i) end ensure f.close end end private # ELF format def read_elf(f) # 0-3 "\177ELF" @file_format = :elf # 4 case f.read(1).unpack('C')[0] when 1 @bits = 32 when 2 @bits = 64 else raise 'Invalid ELF class' end # 5 case f.read(1).unpack('C')[0] when 1 @endian = :little pack_type_short = 'v' when 2 @endian = :big pack_type_short = 'n' else raise 'Invalid ELF byte order' end # 6 raise 'Invalid ELF header version' if f.read(1) != "\x01" # 16-17 f.seek(16, IO::SEEK_SET) raise 'Invalid ELF filetype' if f.read(2).unpack(pack_type_short)[0] != 3 # 18-19 case archtype = f.read(2).unpack(pack_type_short)[0] when 2 @cpu = :sparc when 3 @cpu = :i386 when 15 @cpu = :parisc when 18 @cpu = :sparc32plus when 20 @cpu = :ppc when 21 @cpu = :ppc64 when 22 @cpu = :s390 when 43 @cpu = :sparcv9 when 50 @cpu = :ia64 when 62 @cpu = :x86_64 else raise "Invalid ELF archtype: #{archtype}" end end # PE/COFF format def read_pe(f) # 0-1 "MZ" @file_format = :pe # 60-63 f.seek(60, IO::SEEK_SET) pe_offset = f.read(4).unpack('V')[0] # read PE signature f.seek(pe_offset) raise 'invalid pe format' if f.read(4) != "PE\000\000" # read COFF header case machine = f.read(2).unpack('v')[0] when 0x014c @cpu = :i386 @endian = :little @bits = 32 when 0x0200 @cpu = :ia64 @endian = :little @bits = 64 when 0x8664 @cpu = :x86_64 @endian = :little @bits = 64 else raise "Invalid coff machine: #{machine}" end end # HP-UX PA-RISC(32 bit) def read_parisc(f) # 0-1 system_id - CPU_PA_RISC1_1 @file_format = :pa_risc # 2-3 a_magic - SHL_MAGIC raise 'invalid a_magic' if f.read(2).unpack('n')[0] != 0x10e @bits = 32 @endian = :big @cpu = :parisc end # Big-endian Mach-O File def read_mach_o_be(f) @file_format = :mach_o @endian = :big case f.read(2) when "\xfa\xce" # feedface @cpu = :ppc @bits = 32 when "\xfa\xcf" # feedfacf @cpu = :ppc64 @bits = 64 else raise "unknown file format" end end def read_mach_o_le(f, bits) @file_format = :mach_o @endian = :little raise 'unknown file format' if f.read(2) != "\xed\xfe" case bits when 32 @cpu = :i386 @bits = 32 when 64 @cpu = :x86_64 @bits = 64 end end def read_mach_o_unversal(f) raise 'unknown file format' if f.read(2) != "\xba\xbe" # cafebabe @file_format = :universal nfat_arch = f.read(4).unpack('N')[0] @cpu = [] @endian = [] @bits = [] nfat_arch.times do case cputype = f.read(4).unpack('N')[0] when 7 @cpu << :i386 @endian << :little @bits << 32 when 7 + 0x01000000 @cpu << :x86_64 @endian << :little @bits << 64 when 18 @cpu << :ppc @endian << :big @bits << 32 when 18 + 0x01000000 @cpu << :ppc64 @endian << :big @bits << 64 else raise "Unknown mach-o cputype: #{cputype}" end f.seek(4 * 4, IO::SEEK_CUR) end end end class OraConf attr_reader :cc_is_gcc attr_reader :version attr_reader :cflags attr_reader :libs def initialize raise 'use OraConf.get instead' end def self.get original_CFLAGS = $CFLAGS original_defs = $defs ic_dir = nil begin # check Oracle instant client if with_config('instant-client') puts < 1 arch_types = so.cpu[0..-2].join(', ') + ' and ' + so.cpu[-1].to_s else arch_types = so.cpu[0] end puts " skip: #{file} is for #{arch_types} cpu." false end else if so.cpu == this_cpu true else puts " skip: #{file} is for #{so.cpu} cpu." false end end end end glob_name = "#{oci_basename}.#{so_ext}#{oci_glob_postfix}" ld_path = nil file = nil @@ld_envs.each do |env| if ENV[env].nil? puts " #{env} is not set." next end puts " #{env}... " ld_path, file = check_lib_in_path(ENV[env], glob_name, check_proc) break if ld_path end if ld_path.nil? case RUBY_PLATFORM when /linux/ open("|/sbin/ldconfig -p") do |f| print " checking ld.so.conf... " STDOUT.flush while line = f.gets if line =~ /libclntsh\.so\..* => (\/.*)\/libclntsh\.so\.(.*)/ file = "#$1/libclntsh.so.#$2" path = $1 next if (check_proc && !check_proc.call(file)) ld_path = path puts "yes" break end end puts "no" end when /solaris/ if is_32bit crle_cmd = 'crle' else crle_cmd = 'crle -64' end open('|env LANG=C /usr/bin/' + crle_cmd) do |f| while line = f.gets if line =~ /Default Library Path[^:]*:\s*(\S*)/ puts " checking output of `#{crle_cmd}'... " ld_path, file = check_lib_in_path($1, glob_name, check_proc) break end end end when /darwin/ fallback_path = ENV['DYLD_FALLBACK_LIBRARY_PATH'] if fallback_path.nil? puts " DYLD_FALLBACK_LIBRARY_PATH is not set." else puts " checking DYLD_FALLBACK_LIBRARY_PATH..." ld_path, file = check_lib_in_path(fallback_path, glob_name, check_proc) end if ld_path.nil? puts " checking OCI_DIR..." ld_path, file = check_lib_in_path(ENV['OCI_DIR'], glob_name, check_proc) if ld_path puts " checking dependent shared libraries in #{file}..." open("|otool -L #{file}") do |f| f.gets # discard the first line while line = f.gets line =~ /^\s+(\S+)/ libname = $1 case libname when /^@rpath\/libclntsh\.dylib/, /^@rpath\/libnnz\d\d\.dylib/, /^@loader_path\/libnnz\d\d\.dylib/ # No need to check the real path. # The current instant client doesn't use @rpath or @loader_path. when /\/libclntsh\.dylib/, /\/libnnz\d\d.dylib/ raise < 0 puts " #{file} looks like an instant client." return ld_path end end puts " #{file} looks like a full client." end nil end def self.check_lib_in_path(paths, glob_name, check_proc) return nil if paths.nil? paths.split(File::PATH_SEPARATOR).each do |path| next if path.nil? or path == '' print " checking #{path}... " path.gsub!(/\\/, '/') if /mswin32|mswin64|cygwin|mingw32|bccwin32/ =~ RUBY_PLATFORM files = Dir.glob(File.join(path, glob_name)) if files.empty? puts "no" next end STDOUT.flush next if (check_proc && !check_proc.call(files[0])) puts "yes" return path, files[0] end nil end def init check_cc() @cc_is_gcc = check_cc_is_gcc() @lp64 = check_lp64() check_system_header() check_ruby_header() end def check_cc print "checking for cc... " STDOUT.flush if try_run("int main() { return 0; }") puts "ok" else puts "ng" raise "C compiler doesn't work correctly." end end # check_cc def check_cc_is_gcc # bcc defines __GNUC__. why?? return false if RUBY_PLATFORM =~ /bccwin32/ print "checking for gcc... " STDOUT.flush if macro_defined?("__GNUC__", "") print "yes\n" return true else print "no\n" return false end end # check_cc_is_gcc def check_lp64 print "checking for LP64... " STDOUT.flush if try_run("int main() { return sizeof(long) == 8 ? 0 : 1; }") puts "yes" true else puts "no" false end end # check_lp64 def check_system_header if not have_header('sys/types.h') raise <= 1000 oci_h = "#{@oracle_home}/rdbms/public/oci.h" else oci_h = "#{@oracle_home}/rdbms/demo/oci.h" end unless File.exist?(oci_h) raise "'#{oci_h}' does not exists. Install 'Oracle Call Interface' component." end raise 'Cannot get proper cflags.' end cflags ensure $CFLAGS = original_CFLAGS end end # get_cflags end end # OraConf for Instant Client class OraConfIC < OraConf def initialize(ic_dir) init if ic_dir =~ /^\/usr\/lib(?:64)?\/oracle\/(\d+(?:\.\d+)*)\/client(64)?\/lib(?:64)?/ # rpm package # x86 rpms after 11.1.0.7.0: # library: /usr/lib/oracle/X.X/client/lib/ # include: /usr/include/oracle/X.X/client/ # # x86_64 rpms after 11.1.0.7.0: # library: /usr/lib/oracle/X.X/client64/lib/ # include: /usr/include/oracle/X.X/client64/ # # x86 rpms before 11.1.0.6.0: # library: /usr/lib/oracle/X.X.X.X/client/lib/ # include: /usr/include/oracle/X.X.X.X/client/ # # x86_64 rpms before 11.1.0.6.0: # library: /usr/lib/oracle/X.X.X.X/client64/lib/ # include: /usr/include/oracle/X.X.X.X/client64/ # # third-party x86_64 rpms(*1): # library: /usr/lib64/oracle/X.X.X.X/client/lib/ # or /usr/lib64/oracle/X.X.X.X/client/lib64/ # include: /usr/include/oracle/X.X.X.X/client/ # # *1 These had been used before Oracle released official x86_64 rpms. # lib_dir = ic_dir inc_dir = "/usr/include/oracle/#{$1}/client#{$2}" else # zip package lib_dir = ic_dir inc_dir = "#{ic_dir}/sdk/include" end if RUBY_PLATFORM =~ /mswin32|mswin64|cygwin|mingw32|bccwin32/ # when Windows unless File.exist?("#{ic_dir}/sdk/lib/msvc/oci.lib") raise <