##############################################################
# extconf.rb for tcltklib
# release date: 2009-07-15
##############################################################
require 'mkmf'

TkLib_Config = {}
TkLib_Config['search_versions'] = 
  %w[8.9 8.8 8.7 8.6 8.5 8.4 8.3 8.2 8.1 8.0 4.2]


##############################################################
# use old extconf.rb ?
##############################################################
if with_config('tk-old-extconf')
  require File.join(File.dirname(__FILE__), 'old-extconf.rb')
  exit
end


##############################################################
# check configs
##############################################################
$cleanfiles << 'config_list'
config_list_file = File.join(File.dirname(__FILE__),'config_list')
config_list_file_source = config_list_file + '.in'
if !File.exist?(config_list_file) || 
    File.ctime(config_list_file_source) > File.ctime(config_list_file)
  old_config_list_file = config_list_file_source
else
  old_config_list_file = config_list_file
end

current_configs = {'with'=>{}, 'enable'=>{}}

# setup keys by config_list.in
IO.foreach(config_list_file_source){|line|
  line.chomp!
  line.lstrip!
  next if line.empty? || line =~ /^\#/ #
  mode, key, value = line.split(/\s+/, 3)
  value ||= ""
  current_configs[mode][key] = value rescue nil
}

# define current value of keys
IO.foreach(old_config_list_file){|line|
  line.chomp!
  line.lstrip!
  next if line.empty? || line =~ /^\#/ #
  mode, key, value = line.split(/\s+/, 3)
  value ||= ""
  if current_configs[mode] && current_configs[mode].has_key?(key)
    current_configs[mode][key] = value
  end
}

update_flag = false
current_configs['with'].each_key{|key|
  if (value = with_config(key).to_s) != current_configs['with'][key]
    update_flag = true
    current_configs['with'][key] = value
  end
}
current_configs['enable'].each_key{|key|
  if (value = enable_config(key).to_s) != current_configs['enable'][key]
    update_flag = true
    current_configs['enable'][key] = value
  end
}

# update current_configs
if update_flag || !File.exist?(config_list_file)
  open(config_list_file, 'w'){|fobj|
    fobj.print("# values of current configure options (generated by extconf.rb)\n");
    ['with', 'enable'].each{|mode|
      current_configs[mode].each_key{|key|
        fobj.print("#{mode} #{key} #{current_configs[mode][key]}\n")
      }
    }
  }
end

if update_flag
  puts "Configure options for Ruby/Tk may be updated."
  puts "So, delete files which depend on old configs."
  File.delete(*(Dir.glob(File.join(File.dirname(__FILE__), 
                                   "*.#{CONFIG['DLEXT']}"))))
  File.delete(*(Dir.glob(File.join(File.dirname(__FILE__), "*.#{$OBJEXT}"))))
  File.delete(File.join(File.dirname(__FILE__), 'Makefile')) rescue nil

else
  makefile = File.join(File.dirname(__FILE__),'Makefile')
  if File.exist?(makefile) && 
      File.ctime(config_list_file) > File.ctime(makefile)
    # no need to update Makefile
    exit
  end
end

##############################################################
# fuctions
##############################################################
def is_win32?
  /mswin|mingw|cygwin|bccwin/ =~ RUBY_PLATFORM
end

def is_macosx?
 /darwin/ =~ RUBY_PLATFORM
end

def check_tcltk_version(version)
  return [nil, nil] unless version.kind_of? String

  version = version.strip

  tclver = version.dup
  tkver  = version.dup

  dot = major = minor_dot = minor = plvl_dot = plvl = ext = nil

  if version =~ /^(\d)(\.?)(\d)(\.?)(\d*)(.*)$/
    major = $1; minor_dot = $2; minor = $3; plvl_dot = $4; plvl = $5; ext = $6
    dot = ! minor_dot.empty?
    if plvl_dot.empty? && ! plvl.empty?
      minor << plvl
    end
  elsif version =~ /^(\d)(\.?)(\d?)(.*)$/
    major = $1; minor_dot = $2; minor = $3; ext = $4
    dot = ! minor_dot.empty?
  else # unknown -> believe user
    return [tclver, tkver]
  end

  # check Tcl7.6 / Tk4.2 ?
  if major == "7" # Tcl7.6 ( not support Tclversion < 7.6 )
    # Tk4.2
    tkver  = "4" + ((dot)? ".": "") + ((minor.empty)? "": "2") + ext
  elsif major == "4" # Tk4.2 ( not support Tkversion < 4.2 )
    # Tcl7.6
    tclver = "7" + ((dot)? ".": "") + ((minor.empty)? "": "6") + ext
  end

  [tclver, tkver]
end

def get_shlib_versions(major = 8, minor_max = 9, minor_min = 0, ext = "")
  if tclcfg = TkLib_Config["tclConfig_info"]
    major = tclcfg['TCL_MAJOR_VERSION'].to_i
    minor_min = tclcfg['TCL_MINOR_VERSION'].to_i

  elsif TkLib_Config["tcltkversion"]
    tclver, tkver = TkLib_Config["tcltkversion"]
    if tclver =~ /8\.?(\d)(.*)/
      minor_min = $1.to_i
      ext = $2
    else
      # unsupported version
      return [""]
    end
  end

  # if disable-stubs, version is fixed.
  minor_max = minor_min unless TkLib_Config["tcltk-stubs"]

  vers = []
  minor_max.downto(minor_min){|minor|
    vers << "#{major}.#{minor}#{ext}" unless ext.empty?
    vers << "#{major}.#{minor}"
  }

  vers << ""
end

def get_shlib_path_head
  path_head = []
  path_dirs = []

  if TkLib_Config["ActiveTcl"].kind_of?(String)  # glob path
    path_dirs.concat Dir.glob(TkLib_Config["ActiveTcl"]).sort.reverse
  end

  if is_win32?
    if TkLib_Config["ActiveTcl"]
      path_head.concat ["c:/ActiveTcl", "c:/Program Files/ActiveTcl"]
    end
    path_head.concat [
      "c:/Tcl", "c:/Program Files/Tcl",
      "/Tcl", "/Program Files/Tcl"
    ]
    path_head.each{|dir| path_dirs << "#{dir}"}

  else
    [
      '/opt', '/pkg', '/share', 
      '/usr/local/opt', '/usr/local/pkg', '/usr/local/share', '/usr/local',
      '/usr/opt', '/usr/pkg', '/usr/share', '/usr/contrib', '/usr'
    ].each{|dir|
      next unless File.directory?(dir)

      path_dirs << "#{dir}/lib"
      path_dirs << "#{dir}" unless Dir.glob("#{dir}/lib*.*").empty?

      dirnames = []
      if TkLib_Config["ActiveTcl"]
        dirnames.concat ["ActiveTcl","activeTcl","Activetcl","activetcl"]
      end
      dirnames.concat ["TclTk","Tcl_Tk","Tcl-Tk","tcltk","tcl_tk","tcl-tk"]

      dirnames.each{|name|
        path_dirs << "#{dir}/#{name}" if File.directory?("#{dir}/#{name}")
        path_head << "#{dir}/#{name}" unless Dir.glob("#{dir}/#{name}[-89_]*").empty?
      }
    }
  end

  unless TkLib_Config["space-on-tk-libpath"]
    path_head.delete_if{|path| path =~ / /}
    path_dirs.delete_if{|path| path =~ / /}
  end

  [path_head, path_dirs]
end

def find_macosx_framework
  use_framework = is_macosx? && TkLib_Config["ActiveTcl"]

  use_framework ||= (tcl_hdr = with_config("tcl-framework-header"))
  use_framework ||= (tk_hdr  = with_config("tk-framework-header"))
  tcl_hdr = nil unless tcl_hdr.kind_of? String
  tk_hdr  = nil unless tk_hdr.kind_of? String
  TkLib_Config["tcl-framework-header"] = tcl_hdr
  TkLib_Config["tk-framework-header"]  = tk_hdr

  use_framework ||= (tcl_dir = with_config("tcl-framework-dir"))
  tcl_dir = nil unless tcl_dir.kind_of? String
  if !tcl_dir && tcl_hdr
    # e.g. /Library/Frameworks/Tcl.framework/Headers
    #       ==> /Library/Frameworks/Tcl.framework
    tcl_dir = File.dirname(tcl_hdr.strip.chomp('/'))
  end
  TkLib_Config["tcl-framework-dir"] = tcl_dir

  use_framework ||= (tk_dir = with_config("tk-framework-dir"))
  tk_dir = nil unless tk_dir.kind_of? String
  if !tk_dir && tk_hdr
    # e.g. /Library/Frameworks/Tk.framework/Headers
    #       ==> /Library/Frameworks/Tk.framework
    tk_dir = File.dirname(tk_hdr.strip.chomp('/'))
  end
  TkLib_Config["tk-framework-dir"] = tk_dir

  if tcl_dir && !tk_dir
    tk_dir = File.join(File.dirname(tcl_dir), 'Tk.framework')
    TkLib_Config["tk-framework-dir"] = tk_dir
  elsif !tcl_dir && tk_dir
    tcl_dir = File.join(File.dirname(tk_dir), 'Tcl.framework')
    TkLib_Config["tcl-framework-dir"] = tcl_dir
  end
  if tcl_dir && tk_dir
    TkLib_Config["tcltk-framework"] = File.dirname(tcl_dir) unless TkLib_Config["tcltk-framework"]
    return [tcl_dir, tk_dir]
  end

  # framework is disabled?
  if with_config("tcltk-framework") == false || 
      enable_config("tcltk-framework") == false
   return false
  end

  use_framework ||= (framework_dir = with_config("tcltk-framework"))
  if framework_dir.kind_of? String
    TkLib_Config["tcltk-framework"] = framework_dir.strip.chomp('/')
    return [File.join(TkLib_Config["tcltk-framework"], 'Tcl.framework'), 
            File.join(TkLib_Config["tcltk-framework"], 'Tk.framework')]
  end

  unless enable_config("tcltk-framework", use_framework) ||
      enable_config("mac-tcltk-framework", use_framework)
    TkLib_Config["tcltk-framework"] = false
    return false
  end

  paths = [
    #"~/Library/Frameworks", 
    "/Library/Frameworks",
    "/Network/Library/Frameworks", "/System/Library/Frameworks"
  ]

  paths.map{|dir| dir.strip.chomp('/')}.each{|dir|
    next unless File.directory?(tcldir = File.join(dir, "Tcl.framework"))
    next unless File.directory?(tkdir  = File.join(dir, "Tk.framework"))
    TkLib_Config["tcltk-framework"] = dir
    return [tcldir, tkdir]
  }

  nil
end

def collect_tcltk_defs(tcl_defs_str, tk_defs_str)
  conflicts = [
    'PACKAGE_NAME', 'PACKAGE_TARNAME', 'PACKAGE_VERSION', 
    'PACKAGE_STRING', 'PACKAGE_BUGREPORT'
  ]

  begin
    # Ruby 1.9.x or later
    arch_config_h = RbConfig.expand($arch_hdrdir + "/ruby/config.h")
    if File.exist?(arch_config_h)
      keys = []
      IO.foreach(arch_config_h){|line|
        if line =~ /^#define +([^ ]+)/
            keys << $1
        end
      }
      conflicts = keys
    end
  rescue
    # ignore, use default
  end

  if tcl_defs_str
    tcl_defs = tcl_defs_str.split(/ ?-D/).map{|s|
      s =~ /^([^=]+)(.*)$/
      [$1, $2]
    }
  else
    tcl_defs = []
  end

  if tk_defs_str
    tk_defs = tk_defs_str.split(/ ?-D/).map{|s|
      s =~ /^([^=]+)(.*)$/
      [$1, $2]
    }
  else
    tk_defs = []
  end

  defs = tcl_defs | tk_defs

  defs.delete_if{|name,value|
    conflicts.include?(name) ||
      ( (vtcl = tcl_defs.assoc(name)) && (vtk = tk_defs.assoc(name)) && 
          vtcl != vtk )
  }

  defs.map{|ary| s = ary.join(''); (s.strip.empty?)? "": "-D" << s}.join(' ')
end

def parse_tclConfig(file)
  # check tclConfig.sh/tkConfig.sh
  tbl = Hash.new{|h,k| h[k] = ""}
  return tbl unless file
  IO.foreach(file){|line|
    line.strip!
    next if line !~ /^([^\#=][^=]*)=(['"]|)(.*)\2$/
    key, val = $1, $3
    tbl[key] = val.gsub(/\$\{([^}]+)\}/){|s|
      subst = $1
      (tbl[subst])? tbl[subst]: s
    } rescue nil
  }
  tbl
end

def get_libpath(lib_flag, lib_spec)
  # get libpath fro {TCL,Tk}_LIB_FLAG and {TCL,Tk}_LIB_SPEC
  libpath = lib_spec.gsub(/(#{lib_flag}|-L)/, "").strip
end

def search_tclConfig(*paths) # list of lib-dir or [tcl-libdir, tk-libdir]
  TkLib_Config["tclConfig_paths"] = []
  config_dir = []

  paths.compact!

  fixed_tclConfig = ! paths.empty?

  paths.each{|path|
    if path.kind_of?(Array)
      config_dir << path
    else
      dirs = Dir.glob(path)
      config_dir.concat(dirs.zip(dirs))
    end
  }

  if is_win32?
    if TkLib_Config["ActiveTcl"]
      dirs = []
      if TkLib_Config["ActiveTcl"].kind_of?(String)
        dirs << TkLib_Config["ActiveTcl"]
      end
      dirs.concat [
        "c:/ActiveTcl*/lib", "c:/Activetcl*/lib", 
        "c:/activeTcl*/lib", "c:/activetcl*/lib", 
        "c:/Tcl*/lib", "c:/tcl*/lib", 
        "c:/Program Files/ActiveTcl*/lib", "c:/Program Files/Activetcl*/lib",
        "c:/Program Files/activeTcl*/lib", "c:/Program Files/activetcl*/lib",
        "c:/Program Files/Tcl*/lib", "c:/Program Files/tcl*/lib",
        "/ActiveTcl*/lib", "/Activetcl*/lib", 
        "/activeTcl*/lib", "/activetcl*/lib", 
        "/Tcl*/lib", "/tcl*/lib", 
        "/Program Files/ActiveTcl*/lib", "/Program Files/Activetcl*/lib",
        "/Program Files/activeTcl*/lib", "/Program Files/activetcl*/lib",
        "/Program Files/Tcl*/lib", "/Program Files/tcl*/lib"
      ]
    else
      dirs = [
        "c:/Tcl*/lib", "c:/tcl*/lib", 
        "c:/Program Files/Tcl*/lib", "c:/Program Files/tcl*/lib",
        "/Tcl*/lib", "/tcl*/lib", 
        "/Program Files/Tcl*/lib", "/Program Files/tcl*/lib"
      ]
    end
    dirs.collect{|d| Dir.glob(d)}.flatten!
    dirs |= dirs

    ENV['PATH'].split(';').each{|dir|
      dirs << File.expand_path(File.join(dir, '..', 'lib'))
      dirs << dir
      dirs << File.expand_path(File.join(dir, '..'))
    }

    unless TkLib_Config["space-on-tk-libpath"]
      dirs.delete_if{|path| path =~ / /}
    end

    config_dir.concat(dirs.zip(dirs))

  elsif framework = find_macosx_framework()
    config_dir.unshift(framework)

  else
    if activeTcl = TkLib_Config['ActiveTcl']
      # check latest version at first
      config_dir.concat(Dir.glob(activeTcl).sort.reverse)
    end

    config_dir.concat [
      RbConfig::CONFIG['libdir'],
      File.join(RbConfig::CONFIG['exec_prefix'], 'lib'),
      File.join(RbConfig::CONFIG['prefix'], 'lib'), 
      "/usr/local/opt/lib", "/usr/local/pkg/lib", "/usr/local/share/lib", 
      "/usr/local/lib", "/usr/opt/lib", "/usr/pkg/lib", 
      "/usr/share/lib", "/usr/contrib/lib", "/usr/lib"
    ]

    config_dir.concat [
      '/opt', '/pkg', '/share', 
      '/usr/local/opt', '/usr/local/pkg', '/usr/local/share', '/usr/local',
      '/usr/opt', '/usr/pkg', '/usr/share', '/usr/contrib', '/usr'
    ].map{|dir|
      Dir.glob(dir + '/{TclTk,tcltk,Tcl,tcl,Tk,tk}[87]*/lib')
      Dir.glob(dir + '/{TclTk,tcltk,Tcl,tcl,Tk,tk}[87]*')
      Dir.glob(dir + '/{TclTk,tcltk,Tcl,tcl,Tk,tk}/lib')
      Dir.glob(dir + '/{TclTk,tcltk,Tcl,tcl,Tk,tk}')
    }.flatten!

    ENV['PATH'].split(':').each{|dir|
      config_dir << File.expand_path(File.join(dir, '..', 'lib'))
    }

    # for MacOS X
    #config_dir << "~/Library/Tcl"
    #config_dir.concat(Dir.glob("~/Library/Tcl/*").sort.reverse)
    config_dir << "/Library/Tcl"
    config_dir.concat(Dir.glob("/Library/Tcl/*").sort.reverse)
    config_dir << "/Network/Library/Tcl"
    config_dir.concat(Dir.glob("/Network/Library/Tcl/*").sort.reverse)
    config_dir << "/System/Library/Tcl"
    config_dir.concat(Dir.glob("/System/Library/Tcl/*").sort.reverse)
    [
      #"~/Library/Frameworks", 
      "/Library/Frameworks",
      "/Network/Library/Frameworks", "/System/Library/Frameworks"
    ].each{|framework|
      config_dir << [File.expand_path(File.join(framework, 'Tcl.framework')),
                     File.expand_path(File.join(framework, 'Tk.framework'))]
    }
  end

  tclver, tkver = TkLib_Config['tcltkversion']
  conf = nil

  config_dir.map{|dir|
    if dir.kind_of? Array
      [dir[0].strip.chomp('/'), dir[1].strip.chomp('/')]
    else
      dir.strip.chomp('/')
    end
  }.each{|dir|
    # print("check #{dir} ==>");
    if dir.kind_of? Array
      tcldir, tkdir = dir
    else
      tcldir = tkdir = dir
    end

    ['Config-shared.sh','config-shared.sh','Config.sh','config.sh'].each{|f|
      tclpath = File.join(tcldir, 'tcl' << f)
      tkpath  = File.join(tkdir,  'tk'  << f)
      next if !File.exist?(tclpath) || !File.exist?(tkpath)

      # parse tclConfig.sh/tkConfig.sh
      tclconf = parse_tclConfig(tclpath)
      next if tclver && tclver !~ /^#{tclconf['TCL_MAJOR_VERSION']}(\.?)#{tclconf['TCL_MINOR_VERSION']}/
      tkconf = parse_tclConfig(tkpath)
      next if tkver && tkver !~ /^#{tkconf['TK_MAJOR_VERSION']}(\.?)#{tkconf['TK_MINOR_VERSION']}/

      # find tclConfig.sh & tkConfig.sh
      conf = [tclconf, tkconf] unless conf

      # nativethread check
      unless TkLib_Config["ruby_with_thread"]
        tclconf['TCL_THREADS'] == '1'
        puts "WARNIG: find #{tclpath.inspect}, but it WITH nativethread-support under ruby WITHOUT nativethread-support. So, ignore it."
        TkLib_Config["tcltk-NG-path"] << File.dirname(tclpath)
        next
      end

      #return [tclpath, tkpath]
      # print(" #{[tclpath, tkpath]}");
      TkLib_Config["tclConfig_paths"] << [tclpath, tkpath]
    }

    # print("\n");
  }

  if TkLib_Config["tclConfig_paths"].empty?
    [nil, nil]
  else
    # find tclConfig.sh and tkConfig.sh
    TkLib_Config["tclConfig_info"], TkLib_Config["tkConfig_info"] = conf
    TkLib_Config["tclConfig_paths"][0]
  end
end

def get_tclConfig(tclConfig_file, tclConfig_dir, tkConfig_dir)
  use_tclConfig = (tclConfig_file != false) && 
    (tclConfig_dir != false) && (tkConfig_dir != false)

  tclConfig_file = nil unless tclConfig_file.kind_of? String
  tclConfig_dir = nil unless tclConfig_dir.kind_of? String
  tkConfig_dir = nil unless tkConfig_dir.kind_of? String

  unless tclConfig_dir
    if tclConfig_file
      tclConfig_dir = File.dirname(tclConfig_file)
    elsif tkConfig_dir
      tclConfig_dir = tkConfig_dir
    end
  end
  tkConfig_dir ||= tclConfig_dir

  TkLib_Config["tclConfig-file"] = tclConfig_file
  TkLib_Config["tclConfig-dir"] = tclConfig_dir
  TkLib_Config["tkConfig-dir"] = tkConfig_dir

  unless use_tclConfig
    puts("Don't use [tclConfig.sh, tkConfig.sh]")
  else
    puts("Search tclConfig.sh and tkConfig.sh.")
    if tclConfig_dir
      tclConfig, tkConfig = search_tclConfig([tclConfig_dir, tkConfig_dir])
    else
      tclConfig, tkConfig = search_tclConfig()
    end
    # TclConfig_Info = TkLib_Config["tclConfig_info"]
    # TkConfig_Info  = TkLib_Config["tkConfig_info"]

    if tclConfig && tkConfig
      puts("Use [tclConfig.sh,tkConfig.sh] == ['#{tclConfig}','#{tkConfig}']")
      $LIBPATH |= [File.dirname(tclConfig)]
      $LIBPATH |= [File.dirname(tkConfig)]
      #TkLib_Config["tclConfig_paths"].each{|tclcfg, tkcfg|
      #  $LIBPATH |= [File.dirname(tclcfg)] | [File.dirname(tkcfg)]
      #}
    else
      puts("Fail to find [tclConfig.sh, tkConfig.sh]")
    end
  end

  [tclConfig, tkConfig]
end

def check_NG_path(path_list)
  path_list.find_all{|path| not TkLib_Config["tcltk-NG-path"].include?(path) }
end

def check_shlib_search_path(paths)
  if !paths || paths.empty?
    path_list = []

    #if TkLib_Config["ActiveTcl"]
    #  path_list.concat Dir.glob(TkLib_Config["ActiveTcl"]).sort.reverse
    #end
    if TkLib_Config["ActiveTcl"].kind_of?(String)  # glob path
      path_list.concat Dir.glob(TkLib_Config["ActiveTcl"]).sort.reverse
    end

    vers = get_shlib_versions
    path_head, path_dirs = get_shlib_path_head

    path_list.concat vers.map{|ver|
      path_head.map{|head|
        if ver.empty?
          head + "/lib"
        else
          dirs = []

          if !Dir.glob(head + "-*").empty?
            dirs << head + "-#{ver}/lib" if !Dir.glob(head + "-[89].*").empty?
            dirs << head + "-#{ver.delete('.')}/lib" if !Dir.glob(head + "-[89][0-9]*").empty?
          end

          if !Dir.glob(head + "[_-]*").empty?
            dirs << head + "_#{ver}/lib" if !Dir.glob(head + "_[89].*").empty?
            dirs << head + "-#{ver}/lib" if !Dir.glob(head + "-[89].*").empty?
            dirs << head + "_#{ver.delete('.')}/lib" if !Dir.glob(head + "_[89][0-9]*").empty?
            dirs << head + "-#{ver.delete('.')}/lib" if !Dir.glob(head + "-[89][0-9]*").empty?
          end

          dirs
        end
      }
    }.flatten!

    path_list.concat path_dirs

  else
    # paths is a string with PATH environment style
    path_list = paths.split((is_win32?)? ';': ':')
  end

  path_list = check_NG_path(path_list)

  if is_win32?
    # exist-dir only
    path_list.each{|path|
      path = path.strip; $LIBPATH |= [path] if File.directory?(path)
    }
  else
    path_list.each{|path| $LIBPATH |= [path.strip] }
  end
end

def find_tcl(tcllib, stubs, version, *opt_paths)
  puts "Search Tcl library"
  default_paths = []

  default_paths.concat [
    RbConfig::CONFIG['libdir'],
    File.join(RbConfig::CONFIG['exec_prefix'], 'lib'),
    File.join(RbConfig::CONFIG['prefix'], 'lib'),
    "/usr/local/lib", "/usr/pkg/lib", "/usr/contrib/lib", "/usr/lib"
  ].find_all{|dir| File.directory?(dir)}

  default_paths.concat [
    "c:/Tcl/lib", "c:/Program Files/Tcl/lib",
    "/Tcl/lib", "/Program Files/Tcl/lib"
  ].find_all{|dir| File.directory?(dir)}

  unless TkLib_Config["space-on-tk-libpath"]
    default_paths.delete_if{|path| path =~ / /}
  end

  if (paths = opt_paths.compact).empty?
    if TclConfig_Info['config_file_path']
      # use definisions on tclConfig.sh
      TclConfig_Info['TCL_LIB_SPEC'].sub(TclConfig_Info['TCL_LIB_FLAG'],"").strip.sub("-L","") =~ /("|'|)([^"']+)\1/
      $LIBPATH |= [$2] unless $2.empty?

      unless stubs
        #*** Probably, TCL_LIBS is a subset of TK_LIBS. ***
        unless is_win32? # ignore tclConfig on Windows
          # $LDFLAGS << ' ' << TclConfig_Info['TCL_LIBS']
          # $DLDFLAGS << ' ' << TclConfig_Info['TCL_LIBS']
          $LDFLAGS << ' ' << TclConfig_Info['TCL_LIB_SPEC']
        end
        return true
      end

      if TclConfig_Info['TCL_SUPPORTS_STUBS'] == '0' ||
          TclConfig_Info['TCL_STUB_LIB_SPEC'].strip.empty?
        puts "#{TclConfig_Info['config_file_path']} tells us that your Tcl/Tk library doesn't support stub."
        return false
      else
        #*** Probably, TCL_LIBS is a subset of TK_LIBS. ***
        unless is_win32? # ignore tclConfig on Windows
          # $LDFLAGS << ' ' << TclConfig_Info['TCL_LIBS']
          # $DLDFLAGS << ' ' << TclConfig_Info['TCL_LIBS']
          $LDFLAGS << ' ' << TclConfig_Info['TCL_STUB_LIB_SPEC']
        end
        return true
      end
    end

    paths = check_NG_path(default_paths)
  end

  if stubs
    func = "Tcl_InitStubs"
    lib = "tclstub"
  else
    func = "Tcl_FindExecutable"
    lib = "tcl"
  end

  if version && ! version.empty?
    versions = [version]
  else
    versions = TkLib_Config['search_versions']
  end

  if tcllib
    st = find_library(tcllib, func, *paths)
  else
    st = versions.find { |ver|
           find_library("#{lib}#{ver}", func, *paths) or
           find_library("#{lib}#{ver.delete('.')}", func, *paths) or
           find_library("#{lib}#{ver}g", func, *paths) or
           find_library("#{lib}#{ver.delete('.')}g", func, *paths) or
           find_library("tcl#{ver}", func, *paths) or
           find_library("tcl#{ver.delete('.')}", func, *paths) or
           find_library("tcl#{ver}g", func, *paths) or
           find_library("tcl#{ver.delete('.')}g", func, *paths)
         } || (!version && find_library(lib, func, *paths))
  end

  unless st
    puts("Warning:: cannot find Tcl library. tcltklib will not be compiled (tcltklib is disabled on your Ruby == Ruby/Tk will not work). Please check configure options.")
  end
  st
end

def parse_TK_LIBS(tklibs)
  sfx = "lib|shlib|dll|so"
  re = /(("|')[^"']+\.(#{sfx})\2|[^"' ]+\.(#{sfx})|-l("|')[^"']+\5|-l[^" ]+)/#'

  tklibs.scan(re).map{|lib,|
    if lib =~ /^("|')([^"]+)\.(#{sfx})\1/
        "\"-l#{$2}\""
    elsif lib =~ /([^" ]+)\.(#{sfx})/
        "-l#{$1}"
    else
      lib
    end
  }.join(' ')
end

def find_tk(tklib, stubs, version, *opt_paths)
  puts "Search Tk library"
  default_paths = []

  default_paths.concat [
    RbConfig::CONFIG['libdir'],
    File.join(RbConfig::CONFIG['exec_prefix'], 'lib'),
    File.join(RbConfig::CONFIG['prefix'], 'lib'),
    "/usr/local/lib", "/usr/pkg/lib", "/usr/contrib/lib", "/usr/lib"
  ].find_all{|dir| File.directory?(dir)}

  default_paths.concat [
    "c:/Tcl/lib", "c:/Program Files/Tcl/lib",
    "/Tcl/lib", "/Program Files/Tcl/lib"
  ].find_all{|dir| File.directory?(dir)}

  unless TkLib_Config["space-on-tk-libpath"]
    default_paths.delete_if{|path| path =~ / /}
  end

  if (paths = opt_paths.compact).empty?
    if TkConfig_Info['config_file_path']
      # use definisions on tkConfig.sh
      TkConfig_Info['TK_LIB_SPEC'].sub(TkConfig_Info['TK_LIB_FLAG'],"").strip.sub("-L","") =~ /("|'|)([^"']+)\1/
      $LIBPATH |= [$2] unless $2.empty?

      unless stubs
        unless is_win32? # ignore tclConfig on Windows
          # $LDFLAGS << ' ' << parse_TK_LIBS(TkConfig_Info['TK_LIBS'])
          $LDFLAGS << ' ' << TkConfig_Info['TK_LIBS'] unless is_win32?
          # $DLDFLAGS << ' ' << parse_TK_LIBS(TkConfig_Info['TK_LIBS'])
          $LDFLAGS << ' ' << TkConfig_Info['TK_LIB_SPEC'] unless is_win32?
        end
        return true
      end

      if TkConfig_Info['TK_STUB_LIB_SPEC'].strip.empty?
        puts "#{TkConfig_Info['config_file_path']} tells us that your Tcl/Tk library doesn't support stub."
        return false
      else
        unless is_win32? # ignore tclConfig on Windows
          # $LDFLAGS << ' ' << parse_TK_LIBS(TkConfig_Info['TK_LIBS'])
          $LDFLAGS << ' ' << TkConfig_Info['TK_LIBS']
          # $DLDFLAGS << ' ' << parse_TK_LIBS(TkConfig_Info['TK_LIBS'])
          $LDFLAGS << ' ' << TkConfig_Info['TK_STUB_LIB_SPEC']
        end
        return true
      end
    end

    paths = check_NG_path(default_paths)
  end

  if stubs
    func = "Tk_InitStubs"
    lib = "tkstub"
  else
    func = "Tk_Init"
    lib = "tk"
  end

  if version && ! version.empty?
    versions = [version]
  else
    versions = TkLib_Config['search_versions']
  end

  if tklib
    st = find_library(tklib, func, *paths)
  else
    st = versions.find { |ver|
           find_library("#{lib}#{ver}", func, *paths) or
           find_library("#{lib}#{ver.delete('.')}", func, *paths) or
           find_library("#{lib}#{ver}g", func, *paths) or
           find_library("#{lib}#{ver.delete('.')}g", func, *paths) or
           find_library("tk#{ver}", func, *paths) or
           find_library("tk#{ver.delete('.')}", func, *paths) or
           find_library("tk#{ver}g", func, *paths) or
           find_library("tk#{ver.delete('.')}g", func, *paths)
         } || (!version && find_library(lib, func, *paths))
  end

  unless st
    puts("Warning:: cannot find Tk library. tcltklib will not be compiled (tcltklib is disabled on your Ruby == Ruby/Tk will not work). Please check configure options.")
  end
  st
end

def find_tcltk_header(tclver, tkver)
  base_dir = []

  base_dir.concat [
    File.join(RbConfig::CONFIG['prefix'], 'include'),
    "/usr/local/include", "/usr/pkg/include", "/usr/contrib/include",
    "/usr/include"
  ].find_all{|dir| File.directory?(dir)}

  base_dir.concat [
    "c:/Tcl/include", "c:/Program Files/Tcl/include",
    "/Tcl/include", "/Program Files/Tcl/include"
  ].find_all{|dir| File.directory?(dir)}

  unless TkLib_Config["space-on-tk-libpath"]
    base_dir.delete_if{|path| path =~ / /}
  end

  if TclConfig_Info['TCL_INCLUDE_SPEC'] && 
      have_tcl_h = try_cpp('tcl.h', TclConfig_Info['TCL_INCLUDE_SPEC'])
    $INCFLAGS << " " << TclConfig_Info['TCL_INCLUDE_SPEC']
  elsif have_tcl_h = have_header('tcl.h')
    # find
  else
    if tclver && ! tclver.empty?
      versions = [tclver]
    else
      versions = TkLib_Config['search_versions']
    end
    paths = base_dir.dup
    versions.each{|ver| 
      paths.concat(base_dir.map{|dir|
                     [dir + '/tcl' + ver, dir + '/tcl' + ver.delete('.')]
                   }.flatten)
    }
    have_tcl_h = find_header('tcl.h', *paths)
  end

  if TkConfig_Info['TK_INCLUDE_SPEC'] && 
      have_tk_h = try_cpp('tk.h', TclConfig_Info['TK_INCLUDE_SPEC'])
    $INCFLAGS << " " << TkConfig_Info['TK_INCLUDE_SPEC']
  elsif have_tk_h = have_header('tk.h')
    # find
  else
    if tkver && ! tkver.empty?
      versions = [tkver]
    else
      versions = TkLib_Config['search_versions']
    end
    paths = base_dir.dup
    versions.each{|ver| 
      paths.concat(base_dir.map{|dir|
                     [dir + '/tk' + ver, dir + '/tk' + ver.delete('.')]
                   }.flatten)
    }
    have_tk_h = find_header('tk.h', *paths)
  end

  have_tcl_h && have_tk_h
end

def setup_for_macosx_framework
  # search directory of header files
  if File.exist?(dir = File.join(TkLib_Config["tcltk-framework"], 
                                 'Tcl.framework', 'Headers'))
    TclConfig_Info['TCL_INCLUDE_SPEC'] = "-I#{dir} "
    TclConfig_Info['TK_INCLUDE_SPEC'] = "-I#{File.join(TkLib_Config['tcltk-framework'], 'Tk.framework', 'Headers')} "
  else
    dir = Dir.glob(File.join(TkLib_Config["tcltk-framework"], 
                             'Tcl.framework', '*', 'Headers'))
    TclConfig_Info['TCL_INCLUDE_SPEC'] = "-I#{dir[0]} " unless dir.empty?
    TclConfig_Info['TK_INCLUDE_SPEC'] = "-I#{Dir.glob(File.join(TkLib_Config['tcltk-framework'], 'Tk.framework', '*', 'Headers'))[0]} "
  end

  $LDFLAGS << ' -framework Tk -framework Tcl'

  if TkLib_Config["tcl-framework-header"]
    TclConfig_Info['TCL_INCLUDE_SPEC'] = 
      "-I#{TkLib_Config["tcl-framework-header"]} "
  end
  if TkLib_Config["tk-framework-header"]
    TkConfig_Info['TK_INCLUDE_SPEC'] = 
      "-I#{TkLib_Config["tk-framework-header"]} "
  end
end

def find_X11(*opt_paths)
  defaults =
    [ "/usr/X11*/lib", "/usr/lib/X11*", "/usr/local/X11*", "/usr/openwin/lib" ]
  paths = []
  opt_paths.compact.each{|path| paths.concat(Dir.glob(path.strip.chomp('/')))}
  defaults.compact.each{|path| paths.concat(Dir.glob(path.strip.chomp('/')))}
  st = find_library("X11", "XOpenDisplay", *paths)
  unless st
    puts("Warning:: cannot find X11 library. tcltklib will not be compiled (tcltklib is disabled on your Ruby == Ruby/Tk will not work). Please check configure options. If your Tcl/Tk don't require X11, please try --without-X11.")
  end
  st
end

def search_X_libraries
  if TkConfig_Info['config_file_path']
    # use definitions on tkConfig.sh
    if TkConfig_Info['TK_XINCLUDES'] && TkConfig_Info['TK_XLIBSW'] &&
        !TkConfig_Info['TK_XINCLUDES'].strip.empty? &&
        !TkConfig_Info['TK_XLIBSW'].strip.empty?
      use_X = true && with_config("X11", (! is_win32?))
    else
      use_X = false || with_config("X11", false)
    end
  else
    # depend on configure options
    use_X = with_config("X11", (! is_win32?))
  end

  if use_X
    puts("Use X11 libraries.")
    x11_idir, x11_ldir = dir_config("X11")
    x11_ldir2 = with_config("X11-lib")
    exit unless find_X11(x11_ldir2, x11_ldir)
  end

  use_X
end

def pthread_check()
  tcl_major_ver = nil
  tcl_minor_ver = nil

  # Is tcl-thread given by user ?
  case enable_config("tcl-thread")
  when true
    tcl_enable_thread = true
  when false
    tcl_enable_thread = false
  else
    tcl_enable_thread = nil
  end

  if TclConfig_Info['config_file_path']
    if tcl_enable_thread == true
      puts("Warning: definiton of tclConfig.sh is ignored, because --enable-tcl-thread option is given.")
    elsif tcl_enable_thread == false
      puts("Warning: definition of tclConfig.sh is ignored, because --disable-tcl-thread option is given.")
    else
      # tcl-thread is unknown and tclConfig.sh is given
      if TclConfig_Info['TCL_THREADS']
        tcl_enable_thread = (TclConfig_Info['TCL_THREADS'] == "1")
      else
        tcl_major_ver = TclConfig_Info['TCL_MAJOR_VERSION'].to_i
        tcl_minor_ver = TclConfig_Info['TCL_MINOR_VERSION'].to_i
        if tcl_major_ver < 8 || (tcl_major_ver == 8 && tcl_minor_ver == 0)
          tcl_enable_thread = false
        end
      end

      if tcl_enable_thread == nil
        # cannot find definition
        if tcl_major_ver
          puts("Warning: '#{TclConfig_Info['config_file_path']}' doesn't include TCL_THREADS definition.")
        else
          puts("Warning: '#{TclConfig_Info['config_file_path']}' may not be a tclConfig file.")
        end
        tclConfig = false
      end
    end
  end

  if tcl_enable_thread == nil && !TclConfig_Info['config_file_path']
    # tcl-thread is unknown and tclConfig is unavailable
    begin
      try_run_available = try_run("int main() { exit(0); }")
    rescue Exception
      # cannot try_run. Is CROSS-COMPILE environment?
      puts(%Q'\
*****************************************************************************
**
** NATIVETHREAD SUPPORT CHECK WARNING:
**
**   We cannot check the consistency of nativethread support between 
**   Ruby and the Tcl/Tk library in your environment (are you perhaps
**   cross-compiling?). If nativethread support for these 2 packages 
**   is inconsistent you may find you get errors when running Ruby/Tk
**   (e.g. hangs or segmentation faults).  We strongly recommend
**   you to check the consistency manually.
**
*****************************************************************************
')
      return true
    end
  end

  if tcl_enable_thread == nil
    # tcl-thread is unknown
    if try_run(<<EOF)
#include <tcl.h>
int main() {
   Tcl_Interp *ip;
   ip = Tcl_CreateInterp();
   exit((Tcl_Eval(ip, "set tcl_platform(threaded)") == TCL_OK)? 0: 1);
}
EOF
      tcl_enable_thread = true
    elsif try_run(<<EOF)
#include <tcl.h>
static Tcl_ThreadDataKey dataKey;
int main() { exit((Tcl_GetThreadData(&dataKey, 1) == dataKey)? 1: 0); }
EOF
      tcl_enable_thread = true
    else
      tcl_enable_thread = false
    end
  end

  # check pthread mode
  if (TkLib_Config["ruby_with_thread"])
    # ruby -> enable
    unless tcl_enable_thread
      # ruby -> enable && tcl -> disable
      puts(%Q'\
*****************************************************************************
**
** NATIVETHREAD SUPPORT MODE WARNING:
**
**   Ruby is compiled with --enable-pthread, but your Tcl/Tk library 
**   seems to be compiled without nativethread support. Although you can 
**   create the tcltklib library, this combination may cause errors (e.g. 
**   hangs or segmentation faults). If you have no reason to keep the 
**   current nativethread support status, we recommend you reconfigure and 
**   recompile the libraries so that both or neither support nativethreads.
**
**   If you want change the status of nativethread support, please recompile
**   Ruby without "--enable-pthread" configure option (If you use Ruby 1.9.x
**   or later, you cannot remove this option, because it requires native-
**   thread support.) or recompile Tcl/Tk with "--enable-threads" configure 
**   option (if your Tcl/Tk is later than or equal to Tcl/Tk 8.1).
**
*****************************************************************************
')
    end

    # ruby -> enable && tcl -> enable/disable
    if tcl_enable_thread
      $CPPFLAGS += ' -DWITH_TCL_ENABLE_THREAD=1'
    else
      $CPPFLAGS += ' -DWITH_TCL_ENABLE_THREAD=0'
    end

    return true

  else
    # ruby -> disable
    if tcl_enable_thread
      # ruby -> disable && tcl -> enable
      puts(%Q'\
*****************************************************************************
**
** NATIVETHREAD SUPPORT MODE ERROR:
**
**   Ruby is not compiled with --enable-pthread, but your Tcl/Tk
**   library seems to be compiled with nativethread support. This
**   combination may cause frequent hang or segmentation fault
**   errors when Ruby/Tk is working. We recommend that you NEVER
**   create the library with such a combination of nativethread support.
**
**   Please recompile Ruby with the "--enable-pthread" configure option
**   or recompile Tcl/Tk with the "--disable-threads" configure option.
**
*****************************************************************************
')
      $CPPFLAGS += ' -DWITH_TCL_ENABLE_THREAD=1'
      return false
    else
      # ruby -> disable && tcl -> disable
      $CPPFLAGS += ' -DWITH_TCL_ENABLE_THREAD=0'
      return true
    end
  end
end

##############################################################
# main
##############################################################
# check header files
have_func("ruby_native_thread_p", "ruby.h")
have_func("rb_errinfo", "ruby.h")
have_func("rb_safe_level", "ruby.h")
have_func("rb_hash_lookup", "ruby.h")
have_func("rb_proc_new", "ruby.h")
have_func("rb_obj_untrust", "ruby.h")
have_func("rb_obj_taint", "ruby.h")
have_func("rb_set_safe_level_force", "ruby.h")

have_struct_member("struct RArray", "ptr", "ruby.h")
have_struct_member("struct RArray", "len", "ruby.h")

# check libraries
unless is_win32?
  have_library("nsl", "t_open")
  have_library("socket", "socket")
  have_library("dl", "dlopen")
  have_library("m", "log")
end
$CPPFLAGS += ' -D_WIN32' if /cygwin/ =~ RUBY_PLATFORM

# Does ruby have nativethread ?
TkLib_Config["ruby_with_thread"] = 
  macro_defined?('HAVE_NATIVETHREAD', '#include "ruby.h"')

#---------------------------------------------------
# check requirement of Tcl/tk version
tcltk_version = with_config("tcltkversion")
tclver, tkver = 
  TkLib_Config["tcltkversion"] = check_tcltk_version(tcltk_version)
puts("Specified Tcl/Tk version is #{[tclver, tkver]}") if tclver && tkver

# use ActiveTcl ?
#if activeTcl = with_config("ActiveTcl")
if activeTcl = with_config("ActiveTcl", true)
  puts("Use ActiveTcl libraries (if available).")
  activeTcl = '/opt/ActiveTcl*/lib' unless activeTcl.kind_of? String
end
TkLib_Config["ActiveTcl"] = activeTcl

# allow space chars on a libpath
TkLib_Config["space-on-tk-libpath"] = 
  enable_config("space-on-tk-libpath", ! is_win32?)

# enable Tcl/Tk stubs?
=begin
if TclConfig_Info['TCL_STUB_LIB_SPEC'] && TkConfig_Info['TK_STUB_LIB_SPEC'] && 
    !TclConfig_Info['TCL_STUB_LIB_SPEC'].strip.empty? &&
    !TkConfig_Info['TK_STUB_LIB_SPEC'].strip.empty?
  stubs = true
  unless (st = enable_config("tcltk-stubs")).nil?
    stubs &&= st
  end
  unless (st = with_config("tcltk-stubs")).nil?
    stubs &&= st
  end
else
  stubs = enable_config("tcltk-stubs") || with_config("tcltk-stubs")
end
=end
stubs = enable_config("tcltk-stubs") || with_config("tcltk-stubs")
if (TkLib_Config["tcltk-stubs"] = stubs)
  puts("Compile with Tcl/Tk stubs.")
  $CPPFLAGS += ' -DUSE_TCL_STUBS -DUSE_TK_STUBS'
end

# get tclConfig.sh/tkConfig.sh
TkLib_Config["tcltk-NG-path"] = []
tclcfg, tkcfg = get_tclConfig(with_config("tclConfig-file", true),
                              with_config("tclConfig-dir", true),
                              with_config("tkConfig-dir", true))
TclConfig_Info = TkLib_Config["tclConfig_info"] || {}
TkConfig_Info  = TkLib_Config["tkConfig_info"] || {}
TclConfig_Info['config_file_path'] = tclcfg
TkConfig_Info['config_file_path'] = tkcfg

# check tk_shlib_search_path
check_shlib_search_path(with_config('tk-shlib-search-path'))

# search X libraries
use_X = search_X_libraries

# set TCL_DEFS and TK_DEFS
# $CPPFLAGS += " #{TclConfig_Info['TCL_DEFS']}"
# $CPPFLAGS += " #{TkConfig_Info['TK_DEFS']}"
$CPPFLAGS += collect_tcltk_defs(TclConfig_Info['TCL_DEFS'], TkConfig_Info['TK_DEFS'])

# MacOS X Frameworks?
if TkLib_Config["tcltk-framework"]
  puts("Use MacOS X Frameworks.")
  setup_for_macosx_framework
end

# search Tcl/Tk libraries
tk_idir,  tk_ldir  = dir_config("tk")
tcl_idir, tcl_ldir = dir_config("tcl")

tk_ldir2  = with_config("tk-lib")
tcl_ldir2 = with_config("tcl-lib")

tk_ldir_list  = [tk_ldir2,  tk_ldir]
tcl_ldir_list = [tcl_ldir2, tcl_ldir]

tklib = with_config("tklib")
tcllib = with_config("tcllib")

TclConfig_Info['TCL_INCLUDE_SPEC'] = "-I#{tcl_idir.quote}" if tcl_idir
TkConfig_Info['TK_INCLUDE_SPEC']   = "-I#{tk_idir.quote}"  if tk_idir

#---------------------------------------------------

if (TkLib_Config["tcltk-framework"] ||
      ( find_tcltk_header(tclver, tkver) &&
          find_tcl(tcllib, stubs, tclver, *tcl_ldir_list) &&
          find_tk(tklib, stubs, tkver, *tk_ldir_list) ) ) &&
    (stubs || pthread_check())
  # create Makefile

  # for SUPPORT_STATUS
  $INSTALLFILES ||= []
  $INSTALLFILES << ["lib/tkextlib/SUPPORT_STATUS", "$(RUBYLIBDIR)", "lib"]

  # create
  $defs << %[-DRUBY_VERSION=\\"#{RUBY_VERSION}\\"]
  $defs << %[-DRUBY_RELEASE_DATE=\\"#{RUBY_RELEASE_DATE}\\"]

  create_makefile("tcltklib")

  puts "Find Tcl/Tk libraries. Make tcltklib.so which is required by Ruby/Tk."
else
  puts "Can't find proper Tcl/Tk libraries. So, can't make tcltklib.so which is required by Ruby/Tk."
end
