# Matches up platform identifiers by applying transformation rules.
# Uses the 'AttributeMatcher' module to do most of the matching work.
# This file just contains the stuff specific to platform identifiers.

# -----------------------------------------------------------------
# Transformation Rules
# -----------------------------------------------------------------

# [ trigger, transform, cost ]
DEFAULT_RULES = [
   # Backwards compatibility for the 64-bit x86 chips
   ( {'ISA': 'amd64'}, {'ISA': 'i686'}, 1 ),
   ( {'ISA': 'ia64'},  {'ISA': 'i686'}, 3 ),

   # Backwards compatibility for the 32-bit x86 chips
   ( {'ISA': 'i686'},  {'ISA': 'i586'}, 1 ),
   ( {'ISA': 'i586'},  {'ISA': 'i486'}, 1 ),
   ( {'ISA': 'i486'},  {'ISA': 'i386'}, 1 ),

   # A Cygwin system can run Windows executables
   ( {'OS': 'Cygwin'}, {'OS': 'Windows'}, 0 ),

   # FreeBSD has Linux-compatibility mode, right?
   ( {'OS': 'FreeBSD'}, {'OS': 'Linux'}, 4 ),
]

DEFAULT_HOST_COMPONENT_NAMES = [ 'OS', 'ISA' ]

# -----------------------------------------------------------------
# Convert ['Linux', 'i386'] to {'OS'=>'Linux', 'ISA'=>'i386'}
# -----------------------------------------------------------------

def createHostInfoFromComponents(components, componentNames=DEFAULT_HOST_COMPONENT_NAMES):
   hostInfo = {}
   for i in range(len(components)):
      component = components[i]
      if (component != None and component != ''):
         componentName = componentNames[i]
         hostInfo[componentName] = component
   return hostInfo

# -----------------------------------------------------------------
# Convert 'Linux-i386' to {'OS'=>'Linux', 'ISA'=>'i386'}
# -----------------------------------------------------------------

def createHostInfoFromString(hostString, delimiter='-', componentNames=DEFAULT_HOST_COMPONENT_NAMES):
   components = hostString.split(delimiter)
   return createHostInfoFromComponents(components, componentNames)

# -----------------------------------------------------------------
# Convert {'OS'=>'Linux', 'ISA'=>'i386'} to 'Linux-i386'
# -----------------------------------------------------------------

def createHostStringFromInfo(hostInfo, delimiter='-', componentNames=DEFAULT_HOST_COMPONENT_NAMES):
   components = []
   for componentName in componentNames:
      component = hostInfo[componentName]
      if (component == None): component = ''
      components.push(component)
   return delimiter.join(components)

# -----------------------------------------------------------------
# Create a list of 'available target' entries given a list of files
# -----------------------------------------------------------------

def createHostTargets(strings, pattern, delimiter='-', componentNames=DEFAULT_HOST_COMPONENT_NAMES):
   from HashableMap import HashableMap
   targets  = []
   mappings = {}
   for string in strings:
      match = pattern.match(string)
      if (match != None):
         hostString = match.group(1)
         target = createHostInfoFromString(hostString, delimiter, componentNames)
         targets.append(target)
         mappings[HashableMap(target)] = string
   return (targets, mappings)

# -----------------------------------------------------------------
# Find a matching target for the given host information
# -----------------------------------------------------------------

def match(hostInfo, targets, rules=DEFAULT_RULES):
   import AttributeMatcher
   return AttributeMatcher.match([hostInfo], targets, rules)

# -----------------------------------------------------------------
# Detect current host information
# -----------------------------------------------------------------

ALIASES = {
   'OS': {
      'cygwin': 'Cygwin',
      'GNU/Linux': 'Linux'
   },
   'ISA': {
      'x86_64': 'amd64',
   }
}

def detectDefaultHostInfo():
   """
   Try to automatically detect the current platform.  On Windows, we just
   return { 'OS' : 'Windows' }, otherwise we'll run 'uname'.
   """
   # If we're on Windows, then we can't use 'uname'.  Return right away
   import sys;
   if sys.platform == 'win32':
      return { 'OS': 'Windows' }

   import commands;
   hostInfo = {}

   # OS: (uname -o)
   (retCode, hostOs) = commands.getstatusoutput('uname -o')
   if retCode == 0:
      hostInfo['OS'] = hostOs

   # ISA: Try (uname -m, uname -p, uname -i)
   processorFlags = ['m', 'p', 'i']
   for flag in processorFlags:
      (retCode, arch) = commands.getstatusoutput('uname -' + flag)
      # If we get something other than 'unknown', then we'll take it
      if retCode == 0 and arch.lower() != 'unknown':
         hostInfo['ISA'] = arch
         break

   # Use the ALIASES map to translate names into ones we can handle
   for (component, renameTable) in ALIASES.iteritems():
      currentName = hostInfo[component]
      newName = renameTable.get(currentName, currentName)
      hostInfo[component] = newName

   return hostInfo

