# -*- mode: python -*- # ET build script # TTimo # http://scons.sourceforge.net import sys, os, time, commands, re, pickle, StringIO, popen2, commands, pdb, zipfile, string, tempfile import SCons import scons_utils conf_filename='site.conf' # choose configuration variables which should be saved between runs # ( we handle all those as strings ) serialized=[ 'CC', 'CXX', 'JOBS', 'BUILD', 'BUILD_ROOT', 'TARGET_CGAME', 'TARGET_GAME', 'TARGET_UI', 'GAMERANGER' ] # 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 (default gcc) CXX (default g++) Specify C and C++ compilers (defaults gcc and g++) ex: CC="gcc-3.3" You can use ccache and distcc, for instance: CC="ccache distcc gcc" CXX="ccache distcc g++" JOBS (default 1) Parallel build BUILD (default debug) Use debug-all/debug/release to select build settings ex: BUILD="release" debug-all: no optimisations, debugging symbols debug: -O -g release: all optimisations, including CPU target etc. DEDICATED (default 2) Control regular / dedicated type of build: 0 - client 1 - dedicated server 2 - both TARGET_GAME (default 1) Build game module TARGET_CGAME (default 1) Build cgame module TARGET_UI (default 1) Build ui module BUILD_ROOT (default 'build') change the build root directory NOCONF (default 0, not saved) ignore site configuration and use defaults + command line only COPYBINS (default 0, not saved) copy the binaries in a ready-to-release format BUILDMPBIN (default 0, not saved) build mp_bin.pk3 using bin/ directories and game binaries for all platforms BUILDBUNDLE (default 0, not saved) create mac bundle files """ ) # end help --------------------------------------- # sanity ----------------------------------------- EnsureSConsVersion( 0, 96 ) # end sanity ------------------------------------- # system detection ------------------------------- # CPU type cpu = commands.getoutput('uname -m') dll_cpu = '???' # grmbl, alternative naming for .so exp = re.compile('.*i?86.*') if exp.match(cpu): cpu = 'x86' dll_cpu = 'i386' else: cpu = commands.getoutput('uname -p') if ( cpu == 'powerpc' ): cpu = 'ppc' dll_cpu = cpu else: cpu = 'cpu' dll_cpu = cpu OS = commands.getoutput( 'uname -s' ) print 'cpu: ' + cpu # end system detection --------------------------- # default settings ------------------------------- CC = 'gcc' CXX = 'g++' JOBS = '1' BUILD = 'debug' DEDICATED = '2' TARGET_GAME = '1' TARGET_CGAME = '1' TARGET_UI = '1' GAMERANGER = '0' BUILD_ROOT = 'build' NOCONF = '0' COPYBINS = '0' BUILDMPBIN = '0' BUILDBUNDLE = '0' # end default settings --------------------------- # arch detection --------------------------------- def gcc_major(): major = os.popen( CC + ' -dumpversion' ).read().strip() print 'dumpversion: %s' % major major = re.sub('^([^.]+)\\..*$', '\\1', major) print 'gcc major: %s' % major return major gcc3 = (gcc_major() != '2') def gcc_is_mingw(): mingw = os.popen( CC + ' -dumpmachine' ).read() return re.search('mingw', mingw) != None if gcc_is_mingw(): g_os = 'win32' elif cpu == 'ppc': g_os = 'Darwin' else: g_os = 'Linux' print 'os: %s' % g_os # end arch detection ----------------------------- # site settings ---------------------------------- if ( not ARGUMENTS.has_key( 'NOCONF' ) or ARGUMENTS['NOCONF'] != '1' ): 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) else: print 'Site settings ignored' # end site settings ------------------------------ # command line settings -------------------------- for k in ARGUMENTS.keys(): exec_cmd = k + '=\'' + ARGUMENTS[k] + '\'' print 'Command line: ' + exec_cmd exec( exec_cmd ) # end command line settings ---------------------- # save site configuration ---------------------- if ( not ARGUMENTS.has_key( 'NOCONF' ) or ARGUMENTS['NOCONF'] != '1' ): 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 ------------------ # arch detection --------------------------------- def gcc_major(): major = os.popen( CC + ' -dumpversion' ).read().strip() major = re.sub('^([^.]+)\\..*$', '\\1', major) print 'gcc major: %s' % major return major gcc3 = (gcc_major() != '2') def gcc_is_mingw(): mingw = os.popen( CC + ' -dumpmachine' ).read() return re.search('mingw', mingw) != None win32_build = gcc_is_mingw() # end arch detection ----------------------------- # general configuration, target selection -------- g_build = BUILD_ROOT + '/' + BUILD SConsignFile( 'scons.signatures' ) SetOption('num_jobs', JOBS) if ( OS == 'Linux' ): LINK = CC else: LINK = CXX # common flags # BASE + GAME + OPT for game BASECPPFLAGS = [ ] CORECPPPATH = [ ] CORELIBPATH = [ ] CORECPPFLAGS = [ ] GAMECPPFLAGS = [ ] BASELINKFLAGS = [ ] CORELINKFLAGS = [ ] # for release build, further optimisations that may not work on all files OPTCPPFLAGS = [ ] if ( OS == 'Darwin' ): # taken from xcode's default project settings BASECPPFLAGS += [ '-D__MACOS__', '-Wno-long-double', '-arch', 'ppc', '-fasm-blocks', '-fpascal-strings', '-faltivec', '-mcpu=G4', '-mtune=G4', '-Wno-unknown-pragmas' ] BASECPPFLAGS.append( '-pipe' ) # warn all BASECPPFLAGS.append( '-Wall' ) # don't wrap gcc messages BASECPPFLAGS.append( '-fmessage-length=0' ) if ( BUILD == 'debug-all' ): BASECPPFLAGS.append( '-g' ) BASECPPFLAGS.append( '-D_DEBUG' ) elif ( BUILD == 'debug' ): BASECPPFLAGS.append( '-g' ) BASECPPFLAGS.append( '-O1' ) BASECPPFLAGS.append( '-D_DEBUG' ) elif ( BUILD == 'release' ): BASECPPFLAGS.append( '-DNDEBUG' ) if ( OS == 'Linux' ): # -fomit-frame-pointer: gcc manual indicates -O sets this implicitely, # only if that doesn't affect debugging # on Linux, this affects backtrace capability, so I'm assuming this is needed # -finline-functions: implicit at -O3 # -fschedule-insns2: implicit at -O3 # -funroll-loops ? # -mfpmath=sse -msse ? OPTCPPFLAGS = [ '-O3', '-march=i686', '-Winline', '-ffast-math', '-fomit-frame-pointer', '-finline-functions', '-fschedule-insns2' ] elif ( OS == 'Darwin' ): OPTCPPFLAGS = [ '-O3', '-falign-functions=16', '-falign-loops=16', '-finline' ] else: print 'Unknown build configuration ' + BUILD sys.exit(0) # create the build environments g_base_env = Environment( ENV = os.environ, CC = CC, CXX = CXX, LINK = LINK, CPPFLAGS = BASECPPFLAGS, LINKFLAGS = BASELINKFLAGS, CPPPATH = CORECPPPATH, LIBPATH = CORELIBPATH ) scons_utils.SetupUtils( g_base_env ) g_env = g_base_env.Clone() g_env['CPPFLAGS'] += OPTCPPFLAGS g_env['CPPFLAGS'] += CORECPPFLAGS g_env['LINKFLAGS'] += CORELINKFLAGS if ( OS == 'Darwin' ): # configure for dynamic bundle g_env['SHLINKFLAGS'] = '$LINKFLAGS -bundle -flat_namespace -undefined suppress' g_env['SHLIBSUFFIX'] = '.so' # maintain this dangerous optimization off at all times g_env.Append( CPPFLAGS = '-fno-strict-aliasing' ) if ( int(JOBS) > 1 ): print 'Using buffered process output' scons_utils.SetupBufferedOutput( g_env ) # mark the globals GLOBALS = 'g_env OS g_os BUILD gcc3 cpu GAMERANGER' # end general configuration ---------------------- # win32 cross compilation ---------------------- if g_os == 'win32' and os.name != 'nt': # mingw doesn't define the cpu type, but our only target is x86 g_env.Append(CPPDEFINES = '_M_IX86=400') g_env.Append(LINKFLAGS = '-static-libgcc') # scons doesn't support cross-compiling, so set these up manually g_env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1 g_env['WIN32DEFSUFFIX'] = '.def' g_env['PROGSUFFIX'] = '.exe' g_env['SHLIBSUFFIX'] = '.dll' g_env['SHCCFLAGS'] = '$CCFLAGS' # end win32 cross compilation ------------------ # targets ---------------------------------------- toplevel_targets = [] if ( TARGET_GAME == '1' ): Export( 'GLOBALS ' + GLOBALS ) BuildDir( g_build + '/game', '.', duplicate = 0 ) game = SConscript( g_build + '/game/SConscript.game' ) toplevel_targets.append( InstallAs( '#qagame.mp.%s.so' % dll_cpu, game ) ) if ( TARGET_CGAME == '1' ): Export( 'GLOBALS ' + GLOBALS ) BuildDir( g_build + '/cgame', '.', duplicate = 0 ) cgame = SConscript( g_build + '/cgame/SConscript.cgame' ) toplevel_targets.append( InstallAs( '#cgame.mp.%s.so' % dll_cpu, cgame ) ) if ( TARGET_UI == '1' ): Export( 'GLOBALS ' + GLOBALS ) BuildDir( g_build + '/ui', '.', duplicate = 0 ) ui = SConscript( g_build + '/ui/SConscript.ui' ) toplevel_targets.append( InstallAs( '#ui.mp.%s.so' % dll_cpu, ui ) ) class CopyBins(scons_utils.idSetupBase): def copy_bins( self, target, source, env ): for i in source: j = os.path.normpath( os.path.join( os.path.dirname( i.abspath ), '../bin', os.path.basename( i.abspath ) ) ) self.SimpleCommand( 'cp ' + i.abspath + ' ' + j ) if ( OS == 'Linux' ): self.SimpleCommand( 'strip ' + j ) else: # see strip and otool man pages on mac self.SimpleCommand( 'strip -ur ' + j ) copybins_target = [] if ( COPYBINS != '0' ): copy = CopyBins() copybins_target.append( Command( 'copybins', toplevel_targets, Action( copy.copy_bins ) ) ) class MpBin(scons_utils.idSetupBase): def mp_bin( self, target, source, env ): temp_dir = tempfile.mkdtemp( prefix = 'mp_bin' ) self.SimpleCommand( 'cp ../bin/ui* ' + temp_dir ) self.SimpleCommand( 'cp ../bin/cgame* ' + temp_dir ) # zip the mac bundles mac_bundle_dir = tempfile.mkdtemp( prefix = 'mp_mac' ) self.SimpleCommand( 'cp -R "../bin/Wolfenstein ET.app/Contents/Resources/ui_mac.bundle" ' + mac_bundle_dir ) self.SimpleCommand( 'cp -R "../bin/Wolfenstein ET.app/Contents/Resources/cgame_mac.bundle" ' + mac_bundle_dir ) self.SimpleCommand( 'find %s -name \.svn | xargs rm -rf' % mac_bundle_dir ) self.SimpleCommand( 'cd %s ; zip -r -D %s/ui_mac.zip ui_mac.bundle ; mv %s/ui_mac.zip %s/ui_mac' % ( mac_bundle_dir, temp_dir, temp_dir, temp_dir ) ) self.SimpleCommand( 'cd %s ; zip -r -D %s/cgame_mac.zip cgame_mac.bundle ; mv %s/cgame_mac.zip %s/cgame_mac' % ( mac_bundle_dir, temp_dir, temp_dir, temp_dir ) ) mp_bin_path = os.path.abspath( os.path.join ( os.getcwd(), '../etmain/mp_bin.pk3' ) ) self.SimpleCommand( 'cd %s ; zip -r -D %s *' % ( temp_dir, mp_bin_path ) ) if ( BUILDMPBIN != '0' ): mp_bin = MpBin() mpbin_target = Command( 'mp_bin', toplevel_targets + copybins_target, Action( mp_bin.mp_bin ) ) class BuildBundle(scons_utils.idSetupBase): def make_bundle( self, target, source, env ): for i in source: self.SimpleCommand( './makebundle.sh %s' % i ) if ( BUILDBUNDLE != '0' ): build_bundle = BuildBundle() bundle_target = Command( 'build_bundle', toplevel_targets, Action( build_bundle.make_bundle ) ) # end targets ------------------------------------