# scons build script
# http://scons.sourceforge.net

import commands, re, sys, os, pickle, string, popen2
from makeversion import radiant_makeversion, get_version

# to access some internal stuff
import SCons

conf_filename='site.conf'
# there is a default hardcoded value, you can override on command line, those are saved between runs
# we only handle strings
serialized=['CC', 'CXX', 'JOBS', 'BUILD', 'SETUP']

# help -------------------------------------------

Help("""
Usage: scons [OPTIONS] [TARGET] [CONFIG]

[OPTIONS] and [TARGET] are covered in command line options, use scons -H

[CONFIG]: KEY="VALUE" [...]
a number of configuration options saved between runs in the """ + conf_filename + """ file
erase """ + conf_filename + """ to start with default settings again

CC
CXX
	Specify C and C++ compilers (defaults gcc and g++)
	ex: CC="gcc-3.2"
	You can use ccache and distcc, for instance:
	CC="ccache distcc gcc" CXX="ccache distcc g++"

JOBS
	Parallel build
	ex: JOBS="4" is a good setting on SMP machines

BUILD
	Use debug/release to select build settings
	ex: BUILD="release" - default is debug
"""
)

# end help ---------------------------------------
  
# sanity -----------------------------------------

# get a recent python release
# that is broken in current version:
# http://sourceforge.net/tracker/index.php?func=detail&aid=794145&group_id=30337&atid=398971
#EnsurePythonVersion(2,1)
# above 0.90
EnsureSConsVersion( 0, 96 )
print 'SCons ' + SCons.__version__

# end sanity -------------------------------------

# system detection -------------------------------

# TODO: detect Darwin / OSX

# CPU type
g_cpu = commands.getoutput('uname -m')
exp = re.compile('.*i?86.*')
if (g_cpu == 'Power Macintosh' or g_cpu == 'ppc'):
  g_cpu = 'ppc'
elif exp.match(g_cpu):
  g_cpu = 'x86'
else:
  g_cpu = 'cpu'

# OS
OS = commands.getoutput('uname')
print "OS=\"" + OS + "\""

if (OS == 'Linux'):
  # libc .. do the little magic!
  libc = commands.getoutput('/lib/libc.so.6 |grep "GNU C "|grep version|awk -F "version " \'{ print $2 }\'|cut -b -3')

# end system detection ---------------------------

# default settings -------------------------------

CC='gcc'
CXX='g++'
JOBS='1'
BUILD='debug'
INSTALL='#install'
SETUP='0'
g_build_root = 'build'

# end default settings ---------------------------

# site settings ----------------------------------

site_dict = {}
if (os.path.exists(conf_filename)):
	site_file = open(conf_filename, 'r')
	p = pickle.Unpickler(site_file)
	site_dict = p.load()
	print 'Loading build configuration from ' + conf_filename
	for k, v in site_dict.items():
		exec_cmd = k + '=\"' + v + '\"'
		print exec_cmd
		exec(exec_cmd)

# end site settings ------------------------------

# command line settings --------------------------

for k in serialized:
	if (ARGUMENTS.has_key(k)):
		exec_cmd = k + '=\"' + ARGUMENTS[k] + '\"'
		print 'Command line: ' + exec_cmd
		exec(exec_cmd)

# end command line settings ----------------------

# sanity check -----------------------------------

if (SETUP == '1' and BUILD != 'release' and BUILD != 'info'):
  print 'Forcing release build for setup'
  BUILD = 'release'

def GetGCCVersion(name):
  ret = commands.getstatusoutput('%s -dumpversion' % name)
  if ( ret[0] != 0 ):
    return None
  vers = string.split(ret[1], '.')
  if ( len(vers) == 2 ):
    return [ vers[0], vers[1], 0 ]
  elif ( len(vers) == 3 ):
    return vers
  return None

ver_cc = GetGCCVersion(CC)
ver_cxx = GetGCCVersion(CXX)

if ( ver_cc is None or ver_cxx is None or ver_cc[0] < '3' or ver_cxx[0] < '3' or ver_cc != ver_cxx ):
  print 'Compiler version check failed - need gcc 3.x or later:'
  print 'CC: %s %s\nCXX: %s %s' % ( CC, repr(ver_cc), CXX, repr(ver_cxx) )
  Exit(1)

# end sanity check -------------------------------

# save site configuration ----------------------

for k in serialized:
	exec_cmd = 'site_dict[\'' + k + '\'] = ' + k
	exec(exec_cmd)

site_file = open(conf_filename, 'w')
p = pickle.Pickler(site_file)
p.dump(site_dict)
site_file.close()

# end save site configuration ------------------

# general configuration, target selection --------

SConsignFile( "scons.signatures" )

g_build = g_build_root + '/' + BUILD

SetOption('num_jobs', JOBS)

LINK = CXX
# common flags
warningFlags = '-W -Wall -Wcast-align -Wcast-qual -Wno-unused-parameter '
warningFlagsCXX = '-Wno-non-virtual-dtor -Wreorder ' # -Wold-style-cast
CCFLAGS = '' + warningFlags
CXXFLAGS = '-pipe -DQ_NO_STLPORT ' + warningFlags + warningFlagsCXX
CPPPATH = []
if (BUILD == 'debug'):
	CXXFLAGS += '-g -D_DEBUG '
	CCFLAGS += '-g -D_DEBUG '
elif (BUILD == 'release'):
	CXXFLAGS += '-O2 '
	CCFLAGS += '-O2 '
else:
	print 'Unknown build configuration ' + BUILD
	sys.exit( 0 )

LINKFLAGS = ''
if ( OS == 'Linux' ):

  # static
  # 2112833 /opt/gtkradiant/radiant.x86
  # 35282 /opt/gtkradiant/modules/archivezip.so
  # 600099 /opt/gtkradiant/modules/entity.so
  
  # dynamic
  # 2237060 /opt/gtkradiant/radiant.x86
  # 110605 /opt/gtkradiant/modules/archivezip.so
  # 730222 /opt/gtkradiant/modules/entity.so
  
  # EVIL HACK - force static-linking for libstdc++ - create a symbolic link to the static libstdc++ in the root
  os.system("ln -s `g++ -print-file-name=libstdc++.a`")
  
  #if not os.path.exists("./install"):
  #  os.mkdir("./install")
  #os.system("cp `g++ -print-file-name=libstdc++.so` ./install")
  
  CXXFLAGS += '-fno-exceptions -fno-rtti '
  LINKFLAGS += '-Wl,-fini,fini_stub -L. -static-libgcc '
if ( OS == 'Darwin' ):
  CCFLAGS += '-force_cpusubtype_ALL -fPIC '
  CXXFLAGS += '-force_cpusubtype_ALL -fPIC -fno-exceptions -fno-rtti '
  CPPPATH.append('/sw/include')
  CPPPATH.append('/usr/X11R6/include')
  LINKFLAGS += '-L/sw/lib -L/usr/lib -L/usr/X11R6/lib '

CPPPATH.append('libs')

# extend the standard Environment a bit
class idEnvironment(Environment):

  def __init__(self):
    Environment.__init__(self,
      ENV = os.environ, 
      CC = CC,
      CXX = CXX,
      LINK = LINK,
      CCFLAGS = CCFLAGS,
      CXXFLAGS = CXXFLAGS,
      CPPPATH = CPPPATH,
      LINKFLAGS = LINKFLAGS)

  def useGlib2(self):
    self['CXXFLAGS'] += '`pkg-config glib-2.0 --cflags` '
    self['CCFLAGS'] += '`pkg-config glib-2.0 --cflags` '
    self['LINKFLAGS'] += '-lglib-2.0 '
    
  def useXML2(self):
    self['CXXFLAGS'] += '`xml2-config --cflags` '      
    self['CCFLAGS'] += '`xml2-config --cflags` '      
    self['LINKFLAGS'] += '-lxml2 '

  def useGtk2(self):
    self['CXXFLAGS'] += '`pkg-config gtk+-2.0 --cflags` '
    self['CCFLAGS'] += '`pkg-config gtk+-2.0 --cflags` '
    self['LINKFLAGS'] += '-lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lpango-1.0 -lgdk_pixbuf-2.0 '
   
  def useGtkGLExt(self):
    self['CXXFLAGS'] += '`pkg-config gtkglext-1.0 --cflags` '
    self['CCFLAGS'] += '`pkg-config gtkglext-1.0 --cflags` '
    self['LINKFLAGS'] += '-lgtkglext-x11-1.0 -lgdkglext-x11-1.0 '      
    
  def usePNG(self):
    self['CXXFLAGS'] += '`libpng-config --cflags` '
    self['CCFLAGS'] += '`libpng-config --cflags` '
    self['LINKFLAGS'] += '`libpng-config --ldflags` '
    
  def useMHash(self):
    self['LINKFLAGS'] += '-lmhash '
  
  def useZLib(self):
    self['LINKFLAGS'] += '-lz '
    
  def usePThread(self):
    if ( OS == 'Darwin' ):
      self['LINKFLAGS'] += '-lpthread -Wl,-stack_size,0x400000 '
    else:
      self['LINKFLAGS'] += '-lpthread '

  def CheckLDD(self, target, source, env):
    file = target[0]
    if (not os.path.isfile(file.abspath)):
        print('ERROR: CheckLDD: target %s not found\n' % target[0])
        Exit(1)
    # not using os.popen3 as I want to check the return code
    ldd = popen2.Popen3('`which ldd` -r %s' % target[0], 1)
    stdout_lines = ldd.fromchild.readlines()
    stderr_lines = ldd.childerr.readlines()
    ldd_ret = ldd.wait()
    del ldd
    have_undef = 0
    if ( ldd_ret != 0 ):
        print "ERROR: ldd command returned with exit code %d" % ldd_ret
        os.system('rm %s' % target[0])
        Exit()
    for i_line in stderr_lines:
        print repr(i_line)
        regex = re.compile('undefined symbol: (.*)\t\\((.*)\\)\n')
        if ( regex.match(i_line) ):
            symbol = regex.sub('\\1', i_line)
            try:
                env['ALLOWED_SYMBOLS'].index(symbol)
            except:
                have_undef = 1
        else:
            print "ERROR: failed to parse ldd stderr line: %s" % i_line
            os.system('rm %s' % target[0])
            Exit(1)
    if ( have_undef ):
        print "ERROR: undefined symbols"
        os.system('rm %s' % target[0])
        Exit(1)
  
  def SharedLibrarySafe(self, target, source, LIBS=[], LIBPATH='.'):
    result = self.SharedLibrary(target, source, LIBS=LIBS, LIBPATH=LIBPATH)
    if (OS != 'Darwin'):
      AddPostAction(target + '.so', self.CheckLDD)
    return result

g_env = idEnvironment()

# export the globals
GLOBALS = 'g_env INSTALL SETUP g_cpu'

radiant_makeversion('\\ngcc version: %s.%s.%s' % ( ver_cc[0], ver_cc[1], ver_cc[2] ) )

# end general configuration ----------------------

# targets ----------------------------------------

Default('.')

Export('GLOBALS ' + GLOBALS)
BuildDir(g_build, '.', duplicate = 0)
SConscript(g_build + '/SConscript')

# end targets ------------------------------------