#!/usr/bin/env python3

import os
import sys
import subprocess

nm = os.getenv('CROSS','')+'nm'

topnamespace = sys.argv[1]

def warn(w):
  sys.stderr.write('[31;1mwarning: %s[0m\n' % w)
  fwarn.write('%s\n' % w)

done = set()

def doit(d):
  if d in done: return
  done.add(d)

  if os.path.exists(d+'/dependencies'):
    with open(d+'/dependencies') as f:
      for line in f:
        doit(line.strip())

  objs = [d+'/'+fn for fn in os.listdir(d) if fn.endswith('.o')]
  if len(objs) == 0: return
  if len(objs) == 1: obj = objs[0]

  namespace = topnamespace

  dsplit = d.split('/')
  if len(dsplit) >= 4:
    for dpart in dsplit[1:]:
      namespace += '_'+dpart.replace('-','').replace('_','')

  obj2D = {}
  obj2U = {}

  try:
    p = subprocess.Popen([nm,'-AP']+objs,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,universal_newlines=True)
    out,err = p.communicate()
  except Exception as e:
    warn('nm failure: %s' % e)
    return
  if err:
    warn('nm error: %s' % err)
    return
  if p.returncode:
    warn('nm failure: %s' % p.returncode)
    return

  for line in out.splitlines():
    if ':  ' in line: continue # solaris gives empty names for sections
    line = line.split()
    if len(line) < 3: continue
    if not line[0].endswith(':'): continue
    if line[2].startswith('b'): continue
    if line[2].startswith('d'): continue
    if line[2].startswith('f'): continue # solaris
    if line[2].startswith('n'): continue # freebsd
    if line[2].startswith('r'): continue
    if line[2].startswith('s'): continue
    if line[2].startswith('t'): continue

    obj = line[0][:-1]
    if obj not in obj2U:
      obj2U[obj] = []
      obj2D[obj] = []

    symbol = line[1]
    if symbol.startswith('__odr_asan.'): symbol = symbol[11:]
    if symbol.startswith('__x86.get_pc_thunk.'): continue
    if symbol.startswith('__sparc.get_pc_thunk.'): continue
    if symbol.startswith('__sparc_get_pc_thunk.'): continue

    if symbol.startswith(f'_{topnamespace}_'): symbol = symbol[1:]
    ssplit = symbol.split('_')
    if len(ssplit) >= 6 and ssplit[0] == topnamespace:
      ssplit = '_'.join(ssplit[5:])
      if line[2].startswith('U'): obj2U[obj] += [ssplit]
      if line[2].startswith('D'): obj2D[obj] += [ssplit]
      if line[2].startswith('R'): obj2D[obj] += [ssplit]
      if line[2].startswith('S'): obj2D[obj] += [ssplit]
      if line[2].startswith('T'): obj2D[obj] += [ssplit]

    if line[2].startswith('U'): continue

    if symbol == namespace: continue
    if symbol.startswith(namespace+'_'): continue

    warn('%s: symbol %s outside namespace' % (obj,symbol))

  for fn in os.listdir(d):
    if fn.endswith('.c') or fn.endswith('.S'):
      obj = d+'/'+fn[:-2]+'.o'
      if obj not in obj2U: continue

      fnD = []
      fnU = []

      with open(d+'/'+fn) as f:
        for line in f:
          line = line.split()
          if line[:3] == ['//','linker','define']:
            for x in line[3:]:
              fnD += [x]
          if line[:3] == ['//','linker','use']:
            for x in line[3:]:
              fnU += [x]

      if len(fnD) == 0 and len(fnU) == 0: continue
      fnD = set(fnD)
      fnU = set(fnU)
      objD = set(obj2D[obj])
      objU = set(obj2U[obj])

      for s in sorted(fnD):
        if s not in objD:
          warn('%s: linker define %s in %s but not in object file' % (d,s,fn))
      for s in sorted(fnU):
        if s not in objU:
          warn('%s: linker use %s in %s but not in object file' % (d,s,fn))

      for s in sorted(objD):
        if s not in fnD:
          warn('%s: linker define %s not in %s but in object file' % (d,s,fn))
      for s in sorted(objU):
        if s not in fnU:
          warn('%s: linker use %s not in %s but in object file' % (d,s,fn))

for d in sys.argv[2:]:
  d = d.strip()
  with open('%s/result-namespace'%d,'w') as fwarn:
    doit(d)
