#!/usr/bin/env python #/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # * Mupen64plus - configure * # * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * # * Copyright (C) 2009 DarkJeztr * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * # * the Free Software Foundation; either version 2 of the License, or * # * (at your option) any later version. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU General Public License for more details. * # * * # * You should have received a copy of the GNU General Public License * # * along with this program; if not, write to the * # * Free Software Foundation, Inc., * # * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * # * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ import sys import subprocess import os import commands from optparse import OptionParser, OptionGroup, OptionValueError, SUPPRESS_HELP defaultFile = 'configure.gen' defaultOpts = { 'NOBLIGHT':'0','NOJTTL':'0','NO_RESAMP':'0','NODUMMY':'0','PLUGINDBG':'0', 'NOGLN64':'0','NORICE':'0','NOGLIDE':'0', 'NOHLERSP':'0','NOMINPUT':'0', 'DBG_CORE':'0', 'DBG_COUNT':'0', 'DBG_COMPARE':'0', 'DBG_PROFILE':'0', 'DBG' : '0', 'DBGSYM' : '0', 'LIRC' : '1', 'Z64' : '1' , 'GUI' : 'GTK2', 'PREFIX' : '/usr/local', 'SHAREDIR' : '$(PREFIX)/share/mupen64plus', 'BINDIR' : '$(PREFIX)/bin', 'LIBDIR':'$(SHAREDIR)/plugins', 'MANDIR' : '$(PREFIX)/man/man1','PROFILE':'0', 'CONFIGURE.GEN' : 'Included' } defaultHeader = """# configure.gen # File Auto-Generated by ./configure # DO NOT MODIFY """ optionTable = [ # Format: ['option string','make variable','value to assign', # 'Group Identifier','help string'] # ** 'value=None' : option takes argument ['--enable-debuggui', 'DBG','1','OPT','add core debugger'], ['--disable-debuggui','DBG','0','OPT','remove core debugger'], ['--enable-lirc', 'LIRC','1','OPT','add lirc support'], ['--disable-lirc', 'LIRC','0','OPT','remove lirc support'], ['--with-nogui', 'GUI','NONE','OPT','build without GUI'], ['--with-gtk2gui', 'GUI','GTK2','OPT','build with GTK2 GUI'], ['--with-qt4gui', 'GUI','QT4','OPT','build with QT4 GUI'], ['--with-dummy','NODUMMY','0','PLG','enable dummy plugins'], ['--without-dummy','NODUMMY','1','PLG','disable dummy plugins'], ['--with-ricevideo','NORICE','0','PLG',"enable Rice's video plugin"], ['--without-ricevideo','NORICE','1','PLG',"disable Rice's video plugin"], ['--with-gln64video','NOGLN64','0','PLG',"enable glN64 video plugin"], ['--without-gln64video','NOGLN64','1','PLG',"disable glN64 video plugin"], ['--with-glidevideo','NOGLIDE','0','PLG',"enable Glide64 video plugin"], ['--without-glidevideo','NOGLIDE','1','PLG',"disable Glide64 video plugin"], ['--with-z64', 'Z64','1','PLG',"enable Ziggy's lle rsp plugin"], ['--without-z64', 'Z64','0','PLG',"disable Ziggy's lle rsp plugin"], ['--with-azimerrsp','NOHLERSP','0','PLG',"enable Azimer's hle rsp plugin"], ['--without-azimerrsp','NOHLERSP','1','PLG',"disable Azimer's hle rsp plugin"], ['--with-blightinput', 'NOBLIGHT','0','PLG','enable Blight-Input plugin'], ['--without-blightinput', 'NOBLIGHT','1','PLG','disable Blight-Input plugin'], ['--with-mupeninput', 'NOMINPUT','0','PLG','enable Mupen64 Input plugin'], ['--without-mupeninput', 'NOMINPUT','1','PLG','disable Mupen64 Input plugin'], ['--with-jttlaudio','NOJTTL','0','PLG','enable JTTL audio plugin'], ['--without-jttlaudio','NOJTTL','1','PLG','disable JTTL audio plugin'], ['--enable-jttlresample','NO_RESAMP','0','PLG','enable JTTL SINC resampler'], ['--disable-jttlresample','NO_RESAMP','1','PLG','disable JTTL SINC resampler'], ['--enable-debug', 'DBGSYM','1','DBG','add debugging symbols'], ['--disable-debug','DBGSYM','0','DBG','remove debugging symbols'], ['--enable-gprof','PROFILE','1','DBG','enable gprof profiling'], ['--disable-gprof','PROFILE','0','DBG','disable gprof profiling'], ['--enable-dbgcore','DBG_CORE','1','DBG','print r4300 debug printfs'], ['--disable-dbgcore','DBG_CORE','0','DBG','disable r4300 debug printfs'], ['--enable-corecount','DBG_COUNT','1','DBG','print r4300 instruction count (64bit only)'], ['--disable-corecount','DBG_COUNT','0','DBG','disable r4300 instruction count (64bit only)'], ['--enable-corecomp','DBG_COMPARE','1','DBG','enable r4300 core synched debugging'], ['--disable-corecomp','DBG_COMPARE','0','DBG','disable r4300 core synched debugging'], ['--enable-profiling','DBG_PROFILE','1','DBG','dump r4300 dynarec profiling data'], ['--disable-profiling','DBG_PROFILE','0','DBG','disable r4300 dynarec profiling data'], ['--enable-plugindebug','PLUGINDBG','1','DBG','enable extra debugging output for all plugins '], ['--disable-plugindebug','PLUGINDBG','0','DBG','hide extra debugging output for all plugins '], ['--prefix-dir','PREFIX',None,'PTH','sets the prefix for install'], ['--data-dir','SHAREDIR',None,'PTH','path to install shared data'], ['--binary-dir','BINDIR',None,'PTH','path to install the binaries'], ['--plugin-dir','LIBDIR',None,'PTH','path to install the plugins'], ['--man-dir','MANDIR',None,'PTH','path to the manual page'], ] optionHeading = { 'OPT':'Optional Features','PLG':'Plugin Options', 'DBG':'Debugging Options', 'PTH':'Path Options'} def checkDeps(config, options): """Checks dependancies, and disables options requiring unmet deps""" #first assemble the dependency trees pkgTest = ShellTest(['which','pkg-config'],'pkg-config not installed') gtkTest = ShellTest(['pkg-config','gtk+-2.0'],'gtk+-2.0 not installed') gtkTest.addDep(pkgTest) sdlTest = ShellTest(['which','sdl-config'],'SDL development libraries not installed') glewTest = CompileTest('GL/glew.h','-lGLEW','Glew not installed') ftglTest = ShellTest(['pkg-config','ftgl'],'Ftgl not installed') ftglTest.addDep(pkgTest) sdl_flags = commands.getoutput("sdl-config --cflags --libs").split() sdlttfTest = CompileTest('SDL/SDL_ttf.h',sdl_flags+['-lSDL_ttf'],'SDL-TTF development libraries not installed') resampTest = ShellTest(['pkg-config','samplerate'],'libsamplerate not installed') resampTest.addDep(pkgTest) z64Test = ShellTest() z64Test.addDep(glewTest) z64Test.addDep(ftglTest) z64Test.addDep(sdlTest) coreTest = ShellTest() coreTest.addDep(sdlTest) blightTest = ShellTest() blightTest.addDep(sdlTest) blightTest.addDep(sdlttfTest) #The following plugins share the core's dependancy on SDL, but are not tested as #configure aborts testing if the core's deps are not bet # glN64, ricevideo, glide64, jttlaudio, mupen64input dbgTest = CompileTest('dis-asm.h',message='binutils not installed') lircTest = CompileTest('lirc/lirc_client.h','-llirc_client','LIRC not installed') # perform checks # note that the ENTIRE dependency tree should be assembled before any of these tests are performed if not coreTest: print "Cannot build mupen64plus: ", coreTest.failedDep() return False if(config['NOJTTL']=='0'): if(config['NO_RESAMP']=='0') and not resampTest: config['NO_RESAMP']='1' if(options.verbose): sys.stderr.write('Disabling JTTL SYNC resampling support: ') sys.stderr.write(resampTest.failedDep()) sys.stderr.write('\n') if(config['NOBLIGHT'] =='0') and not blightTest: config['NOBLIGHT'] = '1' if(options.verbose): sys.stderr.write('Disabling Blight_Input Plugin: ') sys.stderr.write(blightTest.failedDep()) sys.stderr.write('\n') if(config['Z64']=='1') and not z64Test: config['Z64'] = '0' if(options.verbose): sys.stderr.write('Disabling Z64 Plugin: ') sys.stderr.write(z64Test.failedDep()) sys.stderr.write('\n') if(config['LIRC']=='1') and not lircTest: config['LIRC'] = '0' if(options.verbose): sys.stderr.write('Disabling LIRC support: ') sys.stderr.write(lircTest.failedDep()) sys.stderr.write('\n') if(config['DBG']=='1'): if not dbgTest: config['DBG'] = '0' if(options.verbose): sys.stderr.write('Disabling Graphical Debugger: ') sys.stderr.write(dbgTest.failedDep()) sys.stderr.write('\n') elif config['GUI']=='NONE': config['DBG'] = '0' if(options.verbose): sys.stderr.write('Disabling Graphical Debugger: ') sys.stderr.write('Must have a gui enabled') sys.stderr.write('\n') #make sure we have at least one of each plugin to proceed #Check for video plugins if(config['NODUMMY']=='1' and config['NOGLIDE']=='1'and config['Z64']=='0' and config['NORICE']=='1' and config['NOGLN64']=='1'): print "Cannot build mupen64plus: ", print "No video plugins selected" return False #Check for audio plugins if(config['NODUMMY']=='1' and config['NOJTTL']=='1'): print "Cannot build mupen64plus: ", print "No audio plugins selected" return False #Check for input plugins if(config['NODUMMY']=='1' and config['NOBLIGHT']=='1' and config['NOMINPUT']=='1'): print "Cannot build mupen64plus: ", print "No input plugins selected" return False #Check for rsp plugins if(config['Z64']=='0' and config['NOHLERSP']=='1'): print "Cannot build mupen64plus: ", print "No RSP plugins selected" return False return True def printSettings(config): #print "\nCurrent Settings to write to", defaultFile print "Options:" if config['DBG']=='1': print "\tGraphical Debugger" if config['LIRC']=='1': print "\tLIRC Support" if config['DBGSYM']=='1': print "\tDebug symbols" if config['GUI']=='NONE': print "\tNo GUI" elif config['GUI']=='GTK2': print "\tGTK2 GUI" elif config['GUI']=='QT4': print "\tQT4 GUI" else: print " BAD GUI OPTION" if config['DBG_CORE']=='1': print "\tR4300 debugging output" if config['DBG_COUNT']=='1': print "\tR4300 instruction count output" if config['DBG_COMPARE']=='1': print "\tR4300 core-synched debugging" if config['DBG_PROFILE']=='1': print "\tR4300 dynarec profiling output" if config['PLUGINDBG']=='1': print "\tPlugin debugging output" print "Plugins:" if config['Z64']=='1': print "\tZ64 RSP & Video" if config['NOGLN64']=='0': print "\tglN64 Video" if config['NORICE']=='0': print "\tRice Video" if config['NOGLIDE']=='0': print "\tGlide64 Video" if config['NOJTTL']=='0': if config['NO_RESAMP']=='0': print "\tJTTL Audio with SINC resampler" else: print "\tJTTL Audio without SINC resampler" if config['NOHLERSP']=='0': print "\tAzimer HLE RSP" if config['NOBLIGHT']=='0': print "\tBlight Input" if config['NOMINPUT']=='0': print "\tMupen64 Input" if config['NODUMMY']=='0': print "\tDummy Video\n\tDummy Audio\n\tDummy Input" print "Directories:" print " Install (PREFIX): ", config['PREFIX'] print " Shared (SHAREDIR): ", config['SHAREDIR'] print " Binary (BINDIR): ", config['BINDIR'] print " Plugins (LIBDIR): ", config['LIBDIR'] print " Manual (MANDIR): ", config['MANDIR'] def constructParser(config): parser = OptionParser() for heading in optionHeading: group = OptionGroup(parser, optionHeading[heading]) for entry in optionTable: if entry[3]==heading: if entry[2]==None: # we have an option with an arg group.add_option(entry[0], help=entry[4], metavar="DIR", nargs=1, type='string', action='callback', callback=config.setOption ) else: if entry[2]==defaultOpts[entry[1]]: hlpstr=SUPPRESS_HELP else: hlpstr=entry[4] group.add_option(entry[0], help=hlpstr, action='callback', callback=config.setOption ) parser.add_option_group(group) option = OptionGroup(parser, 'Configure Script Options') option.add_option('-q', '--quiet', help='suppress output', action='store_false', dest='verbose', default=True) option.add_option('-n', '--nosave', help='do not save changes', action='store_false', dest='save', default=True) option.add_option('-f', '--force', help='ignore errors during read', action='store_true', dest='force', default=False) option.add_option('-p', '--previous', help='load previous settings', action='store_true', dest='previous', default=False) option.add_option('-d', '--no-deps', help='skip dependancy checks', action='store_false', dest='checkdeps', default=True) parser.add_option_group(option) return parser class ConfigFile(dict): """Handler for make-compatable config files""" def setOption(self, option, opt, value, parser): for entry in optionTable: if opt==entry[0]: if entry[2]==None: self[entry[1]]=value else: self[entry[1]]=entry[2] return #Handle option mismatches - shouldn't be possible raise OptionValueError( ''.join(['Invalid option: ', opt]) ) def initialize(self, defaultDict=defaultOpts): self.__loaderror = False self.clear() self.update( defaultDict ) def load(self, fileName=defaultFile): FILE = None self.__loaderror = False try: FILE = open(fileName,'r') lineNum = 0 for curLine in FILE: curLine = curLine.split('#')[0] #strip comments lineNum=lineNum+1 if curLine.isspace() or len(curLine) < 1: continue #increment counter and skip line if it's blank if (curLine.count('=') == 1): curLineChunks = curLine.split('=') if (len(curLineChunks[0]) == 0 or curLineChunks[0].isspace()): sys.stderr.write("ERROR: Blank key in {1} line: {0}\n" .replace("{0}", str(lineNum)).replace('{1}',defaultFile) ) self.__loaderror = True else: self[curLineChunks[0].strip()]=curLineChunks[1].strip() else: sys.stderr.write('ERROR: Bad assignment in {1} line: {0}\n' .replace('{0}',str(lineNum)).replace('{1}',defaultFile) ) self.__loaderror = True finally: if(FILE <> None): FILE.close() def save(self, fileName=defaultFile, header=defaultHeader): try: FILE = open(fileName,'w+') FILE.writelines(header) for key in self: FILE.write(''.join([key ,' = ', self[key], '\n'])) finally: if(FILE <> None): FILE.close() def getError(self): return self.__loaderror def __init__(self, fileName=defaultFile, defaultDict=defaultOpts): self.__loaderror = False self.initialize(defaultDict) try: self.load(fileName) except IOError: self.initialize(defaultDict) class ShellTest: """Class to execute some shell command and store the results""" def __init__(self,command='pwd',message=''): self.message = message self.deps = [] self.checked = False self.value = False self.__command = command def result(self): """Process dep to see if it passes or not, and does so recursively""" if(self.checked == False): #print ''.join(["Checking if ",self.message]) self.checked = True try: self.value = (subprocess.call(self.__command, stdout=subprocess.PIPE,stderr=subprocess.PIPE)) == 0 except OSError: self.value = False return self.value def failedDep(self): """Returns string describing the reason this dep failed""" for dep in self.deps: if not dep: return dep.failedDep() return self.message def findDep(self, find): """Returns true if the dependancy 'find' is a dependancy of this dep""" if(self == find): return True for dep in self.deps: if dep.findDep(find): return True return False def addDep(self, dep): self.deps.append(dep) self.checked = False assert(dep.findDep(self) == False) #avoid circular dependancy graphs def __nonzero__(self): for dep in self.deps: if not dep: return False return self.result() class CompileTest(ShellTest): """Class to build a test app and compile it with gcc""" def __init__(self,header='stdio.h',library='-lm',message=''): self.message = message self.deps = [] self.checked = False self.value = False self.__header = header self.__library = library def result(self): """Gets result by creating tmp.c file and running gcc""" if(self.checked == False): #print ''.join(["Checking if ",self.message]) self.checked = True try: try: srcfile = open('./tmp.c','w') main_func = "int main(int argc, char*argv[])" srcfile.write(''.join(['#include<',self.__header,'>\n',main_func,'{}\n'])) finally: srcfile.close() try: # allow __library to be either a string or a list if isinstance(self.__library, list): call = ['gcc','tmp.c']+self.__library+['-o','tmp.out'] else: call = ['gcc','tmp.c', self.__library, '-o','tmp.out'] self.value=(subprocess.call(call, stdout=subprocess.PIPE,stderr=subprocess.PIPE) == 0) except OSError: assert("Could not execute gcc"==0) finally: try: os.remove('./tmp.c') except OSError: pass try: os.remove('./tmp.out') except OSError: pass return self.value def main(): #get options and apply new settings to configChanges configChanges = ConfigFile('',[]) parser = constructParser(configChanges) (options,args) = parser.parse_args() if len(args) > 0: #should be no extra args after parsing parser.error('try "./configure --help"') if(options.previous): config = ConfigFile() #load settings from existing else: config = ConfigFile('') #load default settings config.update(configChanges) #apply settings from command line if(options.checkdeps): if not checkDeps(config, options): #check that current settings have deps met print "\nconfigure: error: You can skip dependancy checks with '-d'" return if(options.verbose): printSettings(config) #do not write settings if we were told not to, or if settings were loaded with errors #unless forced to save if(options.save and (options.force or not config.getError()) ): if(options.verbose): print "\nSaving settings to:", defaultFile config.save() elif(config.getError()): print "NOT SAVING: Due to errors reading", defaultFile print "Use '--force' to override" if __name__ == '__main__': main()