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