SCons: Update SCons build scripts to support Python 3 and SCons 3

Add parentheses to print statements
Use subprocess.check_output instead of commands.getstatusoutput
to run external processes
Use pickle instead of cPickle library
This allows building on Linux distributions that have switched to
python 3 by default but also retains backwards compatibility with
python 2.7
This commit is contained in:
Brock York 2020-06-12 16:16:09 +10:00
parent 1f6d29eac8
commit db2c35a57c
4 changed files with 62 additions and 58 deletions

View file

@ -9,7 +9,6 @@ Import( [ 'utils', 'config', 'settings', 'project' ] )
( libpath, libname ) = os.path.split( project ) ( libpath, libname ) = os.path.split( project )
libname = os.path.splitext( libname )[0] libname = os.path.splitext( libname )[0]
env = Environment( ENV = os.environ ) env = Environment( ENV = os.environ )
settings.SetupEnvironment( env, config['name'] ) settings.SetupEnvironment( env, config['name'] )
proj = utils.vcxproj( os.path.join( GetLaunchDir(), project ) ) proj = utils.vcxproj( os.path.join( GetLaunchDir(), project ) )
@ -29,6 +28,5 @@ except:
objects = [] objects = []
for i in files + add_sources: for i in files + add_sources:
objects.append( emit_func( os.path.join( libpath, i ) ) ) objects.append( emit_func( source=os.path.join( libpath, i ) ) )
Return( 'objects' ) Return( 'objects' )

View file

@ -3,7 +3,7 @@
# TTimo <ttimo@ttimo.net> # TTimo <ttimo@ttimo.net>
# http://scons.org/ # http://scons.org/
import sys, os, platform, cPickle import sys, os, platform, pickle
import utils, config import utils, config
@ -43,10 +43,10 @@ active_configs = []
# load up configurations from the save file # load up configurations from the save file
if ( os.path.exists( conf_filename ) ): if ( os.path.exists( conf_filename ) ):
f = open( conf_filename ) f = open( conf_filename )
print 'reading saved configuration from site.conf' print( 'reading saved configuration from site.conf' )
try: try:
while ( True ): while ( True ):
c = cPickle.load( f ) c = pickle.load( f )
active_configs.append( c ) active_configs.append( c )
except: except:
pass pass
@ -57,12 +57,12 @@ active_configs = config.ConfigParser().parseStatements( active_configs, config_s
assert( len( active_configs ) >= 1 ) assert( len( active_configs ) >= 1 )
# save the config # save the config
print 'saving updated configuration' print( 'saving updated configuration' )
f = open( conf_filename, 'wb' ) f = open( conf_filename, 'wb' )
for c in active_configs: for c in active_configs:
cPickle.dump( c, f, -1 ) pickle.dump( c, f, -1 )
print 'emit build rules' print( 'emit build rules' )
for c in active_configs: for c in active_configs:
print 'emit configuration: %s' % repr( c ) print( 'emit configuration: %s' % repr( c ) )
c.emit() c.emit()

View file

@ -1,5 +1,5 @@
import sys, os, traceback, platform, re, commands, platform, subprocess import sys, os, traceback, platform, re, subprocess, platform
import urllib2, zipfile, shutil, pprint import urllib3, zipfile, shutil, pprint
if __name__ != '__main__': if __name__ != '__main__':
from SCons.Script import * from SCons.Script import *
@ -218,13 +218,14 @@ class Config:
def SetupEnvironment( self, env, config, useGtk = False, useGtkGL = False, useJPEG = False, useZ = False, usePNG = False ): def SetupEnvironment( self, env, config, useGtk = False, useGtkGL = False, useJPEG = False, useZ = False, usePNG = False ):
env['CC'] = self.cc env['CC'] = self.cc
env['CXX'] = self.cxx env['CXX'] = self.cxx
( ret, xml2 ) = commands.getstatusoutput( 'xml2-config --cflags' ) try:
if ( ret != 0 ): xml2 = subprocess.check_output( ['xml2-config', '--cflags'] ).decode( 'utf-8' )
print 'xml2-config failed' except subprocess.CalledProcessError as cpe:
print( 'xml2-config failed with error code {} and output:{}'.format( cpe.returncode, cpe.output ) )
assert( False ) assert( False )
xml2libs = commands.getoutput( 'xml2-config --libs' )
env.ParseConfig( 'xml2-config --libs' ) env.ParseConfig( 'xml2-config --libs' )
baseflags = [ '-pipe', '-Wall', '-fmessage-length=0', '-fvisibility=hidden', xml2.split( ' ' ) ] #Need to strip on xml2-config output. It has a stray \n and that completely screws up scons calling g++
baseflags = [ '-pipe', '-Wall', '-fmessage-length=0', '-fvisibility=hidden', xml2.strip().split( ' ' ) ]
if ( useGtk ): if ( useGtk ):
env.ParseConfig( 'pkg-config gtk+-2.0 --cflags --libs' ) env.ParseConfig( 'pkg-config gtk+-2.0 --cflags --libs' )
@ -409,24 +410,28 @@ class Config:
print( 'Lookup and bundle the PNG and JPEG libraries' ) print( 'Lookup and bundle the PNG and JPEG libraries' )
# radiant.bin doesn't link to jpeg lib directly, grab that from a module # radiant.bin doesn't link to jpeg lib directly, grab that from a module
# Python 2.7 only! # Python 2.7 only!
#module_ldd = subprocess.check_output( 'ldd -r install/modules/image.so', shell = True ) try:
p = subprocess.Popen( 'ldd -r install/modules/image.so', shell = True, stdout = subprocess.PIPE ) module_ldd = subprocess.check_output( ['ldd', '-r', 'install/modules/image.so'] ).decode( 'utf-8' )
module_ldd = p.communicate()[0] except subprocess.CalledProcessError as cpe:
# print( module_ldd ) print( 'ldd failed with error code {} and output:{}'.format(cpe.returncode, cpe.output ))
assert( False )
def find_library( output, libname ): def find_library( output, libname ):
print output for line in output.split( '\n' ):
print libname if libname in line:
match = filter( lambda l : l.find( libname ) != -1, output.split( '\n' ) )[0] return re.split( '.*=> (.*) .*', line)[1]
return re.split( '.*=> (.*) .*', match )[1] return ""
jpeg_path = find_library( module_ldd, 'libjpeg' ) jpeg_path = find_library( module_ldd, 'libjpeg' )
print( 'JPEG library: %s' % repr( jpeg_path ) ) print( 'JPEG library: %s' % repr( jpeg_path ) )
p = subprocess.Popen( 'ldd -r install/modules/imagepng.so', shell = True, stdout = subprocess.PIPE ) try:
module_ldd = p.communicate()[0] module_ldd = subprocess.check_output( ['ldd', '-r', 'install/modules/imagepng.so'] ).decode( 'utf-8' )
except subprocess.CalledProcessError as cpe:
print( 'ldd failed with error code {} and output:{}'.format(cpe.returncode, cpe.output ))
assert( False )
png_path = find_library( module_ldd, 'libpng' ) png_path = find_library( module_ldd, 'libpng' )
print( 'PNG library: %s' % repr( png_path ) ) print( 'PNG library: %s' % repr( png_path ) )
shutil.copy( jpeg_path, 'install' ) shutil.copy( jpeg_path, 'install' )
@ -460,16 +465,16 @@ class ConfigParser:
statement_re = re.compile( '(.*)=(.*)' ) statement_re = re.compile( '(.*)=(.*)' )
value_list_re = re.compile( '([^,]*),?' ) value_list_re = re.compile( '([^,]*),?' )
if ( not statement_re.match( s ) ): if ( not statement_re.match( s ) ):
print 'syntax error (statement match): %s' % repr( s ) print( 'syntax error (statement match): %s' % repr( s ) )
return return
statement_split = statement_re.split( s ) statement_split = statement_re.split( s )
if ( len( statement_split ) != 4 ): if ( len( statement_split ) != 4 ):
print 'syntax error (statement split): %s' % repr( s ) print( 'syntax error (statement split): %s' % repr( s ) )
return return
( foo, name, value, bar ) = statement_split ( foo, name, value, bar ) = statement_split
value_split = value_list_re.split( value ) value_split = value_list_re.split( value )
if ( len( value_split ) < 2 or len( value_split ) % 2 != 1 ): if ( len( value_split ) < 2 or len( value_split ) % 2 != 1 ):
print 'syntax error (value split): %s' % ( repr( value_split ) ) print( 'syntax error (value split): %s' % ( repr( value_split ) ) )
return return
try: try:
value_array = [] value_array = []
@ -479,8 +484,8 @@ class ConfigParser:
value_array.append( value_split.pop() ) value_array.append( value_split.pop() )
value_split.pop() value_split.pop()
except: except:
print traceback.print_exception( sys.exc_type, sys.exc_value, sys.exc_traceback ) print( traceback.print_exception( sys.exc_type, sys.exc_value, sys.exc_traceback ) )
print 'syntax error (value to array): %s' % ( repr( value_split ) ) print( 'syntax error (value to array): %s' % ( repr( value_split ) ) )
return return
return ( name, value_array ) return ( name, value_array )
@ -504,13 +509,13 @@ class ConfigParser:
ret = self._parseStatement( s ) ret = self._parseStatement( s )
if ( ret is None ): if ( ret is None ):
print 'stop statement parse at %s' % repr( s ) print( 'stop statement parse at %s' % repr( s ) )
break break
( name, value_array ) = ret ( name, value_array ) = ret
try: try:
processor = self.operators[name] processor = self.operators[name]
except: except:
print 'unknown operator %s - stop statement parse at %s' % ( repr( name ), repr( s ) ) print( 'unknown operator %s - stop statement parse at %s' % ( repr( name ), repr( s ) ) )
break break
processor( value_array ) processor( value_array )
@ -518,7 +523,7 @@ class ConfigParser:
self.configs.append( self.current_config ) self.configs.append( self.current_config )
# make sure there is at least one config # make sure there is at least one config
if ( len( self.configs ) == 0 ): if ( len( self.configs ) == 0 ):
print 'pushing a default config' print( 'pushing a default config' )
self.configs.append( Config() ) self.configs.append( Config() )
return self.configs return self.configs
@ -533,17 +538,17 @@ class TestConfigParse( unittest.TestCase ):
# test basic config parsing # test basic config parsing
# needs to cleanly stop at the first config statement that is not recognized # needs to cleanly stop at the first config statement that is not recognized
configs = self.parser.parseStatements( None, [ 'game=missionpack', 'config=qvm', 'foobar' ] ) configs = self.parser.parseStatements( None, [ 'game=missionpack', 'config=qvm', 'foobar' ] )
print repr( configs ) print( repr( configs ) )
def testMultiParse( self ): def testMultiParse( self ):
# multiple configs seperated by commas # multiple configs seperated by commas
configs = self.parser.parseStatements( None, [ 'target=server,game,cgame' ] ) configs = self.parser.parseStatements( None, [ 'target=server,game,cgame' ] )
print repr( configs ) print( repr( configs ) )
def testOp( self ): def testOp( self ):
# test the operator for multiple configs # test the operator for multiple configs
configs = self.parser.parseStatements( None, [ 'target=core', 'config=release', 'op=push', 'target=game,cgame,ui', 'config=debug' ] ) configs = self.parser.parseStatements( None, [ 'target=core', 'config=release', 'op=push', 'target=game,cgame,ui', 'config=debug' ] )
print repr( configs ) print( repr( configs ) )
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -3,14 +3,14 @@
# TTimo <ttimo@ttimo.net> # TTimo <ttimo@ttimo.net>
# http://scons.org/ # http://scons.org/
import os, commands, platform, xml.sax, re, string, platform import os, subprocess, platform, xml.sax, re, string, platform
class vcxproj( xml.sax.handler.ContentHandler ): class vcxproj( xml.sax.handler.ContentHandler ):
def __init__( self, filepath ): def __init__( self, filepath ):
self.source_files = [] self.source_files = []
self.misc_files = [] self.misc_files = []
self._files = [] self._files = []
print 'parse %s' % filepath print( 'parse %s' % filepath )
xml.sax.parse( filepath, self ) xml.sax.parse( filepath, self )
def getSourceFiles( self ): def getSourceFiles( self ):
@ -30,7 +30,7 @@ class vcxproj( xml.sax.handler.ContentHandler ):
def startElement( self, name, attrs ): def startElement( self, name, attrs ):
if ( name == 'ClCompile' ): if ( name == 'ClCompile' ):
if ( attrs.has_key('Include') ): if ( 'Include' in attrs.getNames() ):
self._files.append( attrs.getValue('Include') ) self._files.append( attrs.getValue('Include') )
def endDocument( self ): def endDocument( self ):
@ -39,10 +39,10 @@ class vcxproj( xml.sax.handler.ContentHandler ):
if ( platform.system() != 'Windows' ): if ( platform.system() != 'Windows' ):
f = f.replace( '\\', '/' ) f = f.replace( '\\', '/' )
if ( f[-2:] == '.c' or f[-4:] == '.cpp' ): if ( f[-2:] == '.c' or f[-4:] == '.cpp' ):
self.source_files.append( f.encode('ascii') ) self.source_files.append( f )
else: else:
self.misc_files.append( f ) self.misc_files.append( f )
print '%d source files' % len( self.source_files ) print( '%d source files' % len( self.source_files ) )
# action uses LDD to verify that the source doesn't hold unresolved symbols # action uses LDD to verify that the source doesn't hold unresolved symbols
# setup as an AddPostAction of a regular SharedLibrary call # setup as an AddPostAction of a regular SharedLibrary call
@ -53,16 +53,17 @@ def CheckUnresolved( source, target, env ):
# TODO: implement this for FreeBSD # TODO: implement this for FreeBSD
if ( platform.system() == 'FreeBSD' ): if ( platform.system() == 'FreeBSD' ):
return None return None
print 'CheckUnresolved %s' % target[0].abspath print( 'CheckUnresolved %s' % target[0].abspath )
if ( not os.path.isfile( target[0].abspath ) ): if ( not os.path.isfile( target[0].abspath ) ):
print 'CheckUnresolved: %s does not exist' % target[0] print( 'CheckUnresolved: %s does not exist' % target[0] )
return 1 # fail return 1 # fail
( status, output ) = commands.getstatusoutput( 'ldd -r %s' % target[0] ) try:
if ( status != 0 ): stdout = subprocess.check_output( ['ldd', '-r', str(target[0])] ).decode( 'utf-8' )
print 'CheckUnresolved: ldd command failed (exit code %d)' % status except subprocess.CalledProcessError as cpe:
print( 'CheckUnresolved: ldd command failed (exit code {})'.format( cpe.returncode ) )
os.system( 'rm %s' % target[ 0 ] ) os.system( 'rm %s' % target[ 0 ] )
return 1 # fail return 1 # fail
lines = string.split( output, '\n' ) lines = stdout.split( '\n' )
have_undef = 0 have_undef = 0
for i_line in lines: for i_line in lines:
regex = re.compile('undefined symbol: (.*)\t\\((.*)\\)') regex = re.compile('undefined symbol: (.*)\t\\((.*)\\)')
@ -73,8 +74,8 @@ def CheckUnresolved( source, target, env ):
except: except:
have_undef = 1 have_undef = 1
if ( have_undef ): if ( have_undef ):
print output print( output )
print "CheckUnresolved: undefined symbols" print( "CheckUnresolved: undefined symbols" )
os.system('rm %s' % target[0]) os.system('rm %s' % target[0])
return 1 return 1