SConstruct 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. # scons build script
  2. # http://scons.sourceforge.net
  3. import commands, re, sys, os, pickle, string, popen2
  4. from makeversion import radiant_makeversion, get_version
  5. from osx_setup import do_osx_setup
  6. # to access some internal stuff
  7. import SCons
  8. conf_filename='site.conf'
  9. # there is a default hardcoded value, you can override on command line, those are saved between runs
  10. # we only handle strings
  11. serialized=['CC', 'CXX', 'JOBS', 'BUILD', 'SETUP']
  12. # help -------------------------------------------
  13. Help("""
  14. Usage: scons [OPTIONS] [TARGET] [CONFIG]
  15. [OPTIONS] and [TARGET] are covered in command line options, use scons -H
  16. [CONFIG]: KEY="VALUE" [...]
  17. a number of configuration options saved between runs in the """ + conf_filename + """ file
  18. erase """ + conf_filename + """ to start with default settings again
  19. CC
  20. CXX
  21. Specify C and C++ compilers (defaults gcc and g++)
  22. ex: CC="gcc-3.2"
  23. You can use ccache and distcc, for instance:
  24. CC="ccache distcc gcc" CXX="ccache distcc g++"
  25. JOBS
  26. Parallel build
  27. ex: JOBS="4" is a good setting on SMP machines
  28. BUILD
  29. Use debug/release to select build settings
  30. ex: BUILD="release" - default is debug
  31. OSX: use BUILD="info" to generate the set of release files
  32. SETUP
  33. Build a setup - default 0
  34. """
  35. )
  36. # end help ---------------------------------------
  37. # sanity -----------------------------------------
  38. # get a recent python release
  39. # that is broken in current version:
  40. # http://sourceforge.net/tracker/index.php?func=detail&aid=794145&group_id=30337&atid=398971
  41. #EnsurePythonVersion(2,1)
  42. # above 0.90
  43. EnsureSConsVersion( 0, 96 )
  44. print 'SCons ' + SCons.__version__
  45. # end sanity -------------------------------------
  46. # system detection -------------------------------
  47. # TODO: detect Darwin / OSX
  48. # CPU type
  49. g_cpu = commands.getoutput('uname -m')
  50. exp = re.compile('.*i?86.*')
  51. if (g_cpu == 'Power Macintosh' or g_cpu == 'ppc'):
  52. g_cpu = 'ppc'
  53. elif exp.match(g_cpu):
  54. g_cpu = 'x86'
  55. else:
  56. g_cpu = 'cpu'
  57. # OS
  58. OS = commands.getoutput('uname')
  59. print "OS=\"" + OS + "\""
  60. if (OS == 'Linux'):
  61. # libc .. do the little magic!
  62. libc = commands.getoutput('/lib/libc.so.6 |grep "GNU C "|grep version|awk -F "version " \'{ print $2 }\'|cut -b -3')
  63. # end system detection ---------------------------
  64. # default settings -------------------------------
  65. CC='gcc'
  66. CXX='g++'
  67. JOBS='1'
  68. BUILD='debug'
  69. INSTALL='#install'
  70. SETUP='0'
  71. g_build_root = 'build'
  72. # end default settings ---------------------------
  73. # site settings ----------------------------------
  74. site_dict = {}
  75. if (os.path.exists(conf_filename)):
  76. site_file = open(conf_filename, 'r')
  77. p = pickle.Unpickler(site_file)
  78. site_dict = p.load()
  79. print 'Loading build configuration from ' + conf_filename
  80. for k, v in site_dict.items():
  81. exec_cmd = k + '=\"' + v + '\"'
  82. print exec_cmd
  83. exec(exec_cmd)
  84. # end site settings ------------------------------
  85. # command line settings --------------------------
  86. for k in serialized:
  87. if (ARGUMENTS.has_key(k)):
  88. exec_cmd = k + '=\"' + ARGUMENTS[k] + '\"'
  89. print 'Command line: ' + exec_cmd
  90. exec(exec_cmd)
  91. # end command line settings ----------------------
  92. # sanity check -----------------------------------
  93. if (SETUP == '1' and BUILD != 'release' and BUILD != 'info'):
  94. print 'Forcing release build for setup'
  95. BUILD = 'release'
  96. def GetGCCVersion(name):
  97. ret = commands.getstatusoutput('%s -dumpversion' % name)
  98. if ( ret[0] != 0 ):
  99. return None
  100. vers = string.split(ret[1], '.')
  101. if ( len(vers) == 2 ):
  102. return [ vers[0], vers[1], 0 ]
  103. elif ( len(vers) == 3 ):
  104. return vers
  105. return None
  106. ver_cc = GetGCCVersion(CC)
  107. ver_cxx = GetGCCVersion(CXX)
  108. 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 ):
  109. print 'Compiler version check failed - need gcc 3.x or later:'
  110. print 'CC: %s %s\nCXX: %s %s' % ( CC, repr(ver_cc), CXX, repr(ver_cxx) )
  111. Exit(1)
  112. # end sanity check -------------------------------
  113. # save site configuration ----------------------
  114. for k in serialized:
  115. exec_cmd = 'site_dict[\'' + k + '\'] = ' + k
  116. exec(exec_cmd)
  117. site_file = open(conf_filename, 'w')
  118. p = pickle.Pickler(site_file)
  119. p.dump(site_dict)
  120. site_file.close()
  121. # end save site configuration ------------------
  122. # general configuration, target selection --------
  123. SConsignFile( "scons.signatures" )
  124. g_build = g_build_root + '/' + BUILD
  125. SetOption('num_jobs', JOBS)
  126. LINK = CXX
  127. # common flags
  128. warningFlags = '-W -Wall -Wcast-align -Wcast-qual -Wno-unused-parameter '
  129. warningFlagsCXX = '-Wno-non-virtual-dtor -Wreorder ' # -Wold-style-cast
  130. CCFLAGS = '' + warningFlags
  131. CXXFLAGS = '-pipe -DQ_NO_STLPORT ' + warningFlags + warningFlagsCXX
  132. CPPPATH = []
  133. if (BUILD == 'debug'):
  134. CXXFLAGS += '-g -D_DEBUG '
  135. CCFLAGS += '-g -D_DEBUG '
  136. elif (BUILD == 'release'):
  137. CXXFLAGS += '-O2 '
  138. CCFLAGS += '-O2 '
  139. elif ( BUILD == 'info' ):
  140. print 'Preparing OSX release'
  141. ( line, major, minor ) = get_version()
  142. do_osx_setup( major, minor, 'osx-radiant-%s.run' % line )
  143. sys.exit( 0 )
  144. else:
  145. print 'Unknown build configuration ' + BUILD
  146. sys.exit( 0 )
  147. LINKFLAGS = ''
  148. if ( OS == 'Linux' ):
  149. # static
  150. # 2112833 /opt/gtkradiant/radiant.x86
  151. # 35282 /opt/gtkradiant/modules/archivezip.so
  152. # 600099 /opt/gtkradiant/modules/entity.so
  153. # dynamic
  154. # 2237060 /opt/gtkradiant/radiant.x86
  155. # 110605 /opt/gtkradiant/modules/archivezip.so
  156. # 730222 /opt/gtkradiant/modules/entity.so
  157. # EVIL HACK - force static-linking for libstdc++ - create a symbolic link to the static libstdc++ in the root
  158. os.system("ln -s `g++ -print-file-name=libstdc++.a`")
  159. #if not os.path.exists("./install"):
  160. # os.mkdir("./install")
  161. #os.system("cp `g++ -print-file-name=libstdc++.so` ./install")
  162. CXXFLAGS += '-fno-exceptions -fno-rtti '
  163. LINKFLAGS += '-Wl,-fini,fini_stub -L. -static-libgcc '
  164. if ( OS == 'Darwin' ):
  165. CCFLAGS += '-force_cpusubtype_ALL -fPIC '
  166. CXXFLAGS += '-force_cpusubtype_ALL -fPIC -fno-exceptions -fno-rtti '
  167. CPPPATH.append('/sw/include')
  168. CPPPATH.append('/usr/X11R6/include')
  169. LINKFLAGS += '-L/sw/lib -L/usr/lib -L/usr/X11R6/lib '
  170. CPPPATH.append('libs')
  171. # extend the standard Environment a bit
  172. class idEnvironment(Environment):
  173. def __init__(self):
  174. Environment.__init__(self,
  175. ENV = os.environ,
  176. CC = CC,
  177. CXX = CXX,
  178. LINK = LINK,
  179. CCFLAGS = CCFLAGS,
  180. CXXFLAGS = CXXFLAGS,
  181. CPPPATH = CPPPATH,
  182. LINKFLAGS = LINKFLAGS)
  183. def useGlib2(self):
  184. self['CXXFLAGS'] += '`pkg-config glib-2.0 --cflags` '
  185. self['CCFLAGS'] += '`pkg-config glib-2.0 --cflags` '
  186. self['LINKFLAGS'] += '-lglib-2.0 '
  187. def useXML2(self):
  188. self['CXXFLAGS'] += '`xml2-config --cflags` '
  189. self['CCFLAGS'] += '`xml2-config --cflags` '
  190. self['LINKFLAGS'] += '-lxml2 '
  191. def useGtk2(self):
  192. self['CXXFLAGS'] += '`pkg-config gtk+-2.0 --cflags` '
  193. self['CCFLAGS'] += '`pkg-config gtk+-2.0 --cflags` '
  194. self['LINKFLAGS'] += '-lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lpango-1.0 -lgdk_pixbuf-2.0 '
  195. def useGtkGLExt(self):
  196. self['CXXFLAGS'] += '`pkg-config gtkglext-1.0 --cflags` '
  197. self['CCFLAGS'] += '`pkg-config gtkglext-1.0 --cflags` '
  198. self['LINKFLAGS'] += '-lgtkglext-x11-1.0 -lgdkglext-x11-1.0 '
  199. def usePNG(self):
  200. self['CXXFLAGS'] += '`libpng-config --cflags` '
  201. self['CCFLAGS'] += '`libpng-config --cflags` '
  202. self['LINKFLAGS'] += '`libpng-config --ldflags` '
  203. def useMHash(self):
  204. self['LINKFLAGS'] += '-lmhash '
  205. def useZLib(self):
  206. self['LINKFLAGS'] += '-lz '
  207. def usePThread(self):
  208. if ( OS == 'Darwin' ):
  209. self['LINKFLAGS'] += '-lpthread -Wl,-stack_size,0x400000 '
  210. else:
  211. self['LINKFLAGS'] += '-lpthread '
  212. def CheckLDD(self, target, source, env):
  213. file = target[0]
  214. if (not os.path.isfile(file.abspath)):
  215. print('ERROR: CheckLDD: target %s not found\n' % target[0])
  216. Exit(1)
  217. # not using os.popen3 as I want to check the return code
  218. ldd = popen2.Popen3('`which ldd` -r %s' % target[0], 1)
  219. stdout_lines = ldd.fromchild.readlines()
  220. stderr_lines = ldd.childerr.readlines()
  221. ldd_ret = ldd.wait()
  222. del ldd
  223. have_undef = 0
  224. if ( ldd_ret != 0 ):
  225. print "ERROR: ldd command returned with exit code %d" % ldd_ret
  226. os.system('rm %s' % target[0])
  227. Exit()
  228. for i_line in stderr_lines:
  229. print repr(i_line)
  230. regex = re.compile('undefined symbol: (.*)\t\\((.*)\\)\n')
  231. if ( regex.match(i_line) ):
  232. symbol = regex.sub('\\1', i_line)
  233. try:
  234. env['ALLOWED_SYMBOLS'].index(symbol)
  235. except:
  236. have_undef = 1
  237. else:
  238. print "ERROR: failed to parse ldd stderr line: %s" % i_line
  239. os.system('rm %s' % target[0])
  240. Exit(1)
  241. if ( have_undef ):
  242. print "ERROR: undefined symbols"
  243. os.system('rm %s' % target[0])
  244. Exit(1)
  245. def SharedLibrarySafe(self, target, source, LIBS=[], LIBPATH='.'):
  246. result = self.SharedLibrary(target, source, LIBS=LIBS, LIBPATH=LIBPATH)
  247. if (OS != 'Darwin'):
  248. AddPostAction(target + '.so', self.CheckLDD)
  249. return result
  250. g_env = idEnvironment()
  251. # export the globals
  252. GLOBALS = 'g_env INSTALL SETUP g_cpu'
  253. radiant_makeversion('\\ngcc version: %s.%s.%s' % ( ver_cc[0], ver_cc[1], ver_cc[2] ) )
  254. # end general configuration ----------------------
  255. # targets ----------------------------------------
  256. Default('.')
  257. Export('GLOBALS ' + GLOBALS)
  258. BuildDir(g_build, '.', duplicate = 0)
  259. SConscript(g_build + '/SConscript')
  260. # end targets ------------------------------------