256 lines
7.7 KiB
Python
256 lines
7.7 KiB
Python
|
import os
|
||
|
import subprocess
|
||
|
import sys
|
||
|
import time
|
||
|
|
||
|
class BuildError(Exception):
|
||
|
def __init__(self, value):
|
||
|
self.value = value
|
||
|
|
||
|
def __repr__(self):
|
||
|
return repr(self.value)
|
||
|
|
||
|
def runInDir(command, dir=None, verbose=True):
|
||
|
if dir:
|
||
|
olddir = os.getcwd()
|
||
|
os.chdir(dir)
|
||
|
|
||
|
commandStr = " ".join(command)
|
||
|
if verbose:
|
||
|
print(commandStr)
|
||
|
result = os.system(commandStr)
|
||
|
|
||
|
if dir:
|
||
|
os.chdir(olddir)
|
||
|
|
||
|
return result
|
||
|
|
||
|
class Builder:
|
||
|
"""
|
||
|
Base class exposing the Builder interface.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, formatName="", commandName="", programDir=None):
|
||
|
"""
|
||
|
formatName = human readable name for project format (should correspond with Bakefile names)
|
||
|
commandName = name of command line program used to invoke builder
|
||
|
programDir = directory program is located in, if not on the path
|
||
|
"""
|
||
|
|
||
|
self.dir = dir
|
||
|
self.name = commandName
|
||
|
self.formatName = formatName
|
||
|
self.programDir = programDir
|
||
|
self.doSetup()
|
||
|
|
||
|
def doSetup(self):
|
||
|
"""
|
||
|
Do anything special needed to configure the environment to build with this builder.
|
||
|
"""
|
||
|
|
||
|
pass
|
||
|
|
||
|
def isAvailable(self):
|
||
|
"""
|
||
|
Run sanity checks before attempting to build with this format
|
||
|
"""
|
||
|
# Make sure the builder program exists
|
||
|
programPath = self.getProgramPath()
|
||
|
if os.path.exists(programPath):
|
||
|
return True
|
||
|
else:
|
||
|
# check the PATH for the program
|
||
|
# TODO: How do we check if we're in Cygwin?
|
||
|
if sys.platform.startswith("win"):
|
||
|
result = os.system(self.name)
|
||
|
if result == 0:
|
||
|
return True
|
||
|
dirs = os.environ["PATH"].split(":")
|
||
|
|
||
|
for dir in dirs:
|
||
|
if os.path.isfile(os.path.join(dir, self.name)):
|
||
|
return True
|
||
|
|
||
|
else:
|
||
|
result = os.system("which %s" % self.name)
|
||
|
|
||
|
if result == 0:
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
def getProgramPath(self):
|
||
|
if self.programDir:
|
||
|
path = os.path.join(self.programDir, self.name)
|
||
|
if sys.platform.startswith("win"):
|
||
|
path = '"%s"' % path
|
||
|
return path
|
||
|
|
||
|
return self.name
|
||
|
|
||
|
def getProjectFileArg(self, projectFile = None):
|
||
|
result = []
|
||
|
if projectFile:
|
||
|
result.append(projectFile)
|
||
|
return result
|
||
|
|
||
|
def clean(self, dir=None, projectFile=None, options=[]):
|
||
|
"""
|
||
|
dir = the directory containing the project file
|
||
|
projectFile = Some formats need to explicitly specify the project file's name
|
||
|
"""
|
||
|
if self.isAvailable():
|
||
|
args = [self.getProgramPath()]
|
||
|
pfArg = self.getProjectFileArg(projectFile)
|
||
|
if pfArg:
|
||
|
args.extend(pfArg)
|
||
|
args.append("clean")
|
||
|
if options:
|
||
|
args.extend(options)
|
||
|
result = runInDir(args, dir)
|
||
|
return result
|
||
|
|
||
|
return False
|
||
|
|
||
|
def configure(self, dir=None, options=[]):
|
||
|
# if we don't have configure, just report success
|
||
|
return 0
|
||
|
|
||
|
def build(self, dir=None, projectFile=None, targets=None, options=[]):
|
||
|
if self.isAvailable():
|
||
|
args = [self.getProgramPath()]
|
||
|
pfArg = self.getProjectFileArg(projectFile)
|
||
|
if pfArg:
|
||
|
args.extend(pfArg)
|
||
|
# Important Note: if extending args, check it first!
|
||
|
# NoneTypes are not iterable and will crash the clean, build, or install!
|
||
|
# Very very irritating when this happens right at the end.
|
||
|
if options:
|
||
|
args.extend(options)
|
||
|
result = runInDir(args, dir)
|
||
|
return result
|
||
|
|
||
|
return 1
|
||
|
|
||
|
def install(self, dir=None, projectFile=None, options=[]):
|
||
|
if self.isAvailable():
|
||
|
args = [self.getProgramPath()]
|
||
|
pfArg = self.getProjectFileArg(projectFile)
|
||
|
if pfArg:
|
||
|
args.extend(pfArg)
|
||
|
args.append("install")
|
||
|
if options:
|
||
|
args.extend(options)
|
||
|
result = runInDir(args, dir)
|
||
|
return result
|
||
|
|
||
|
return 1
|
||
|
|
||
|
|
||
|
# Concrete subclasses of abstract Builder interface
|
||
|
|
||
|
class GNUMakeBuilder(Builder):
|
||
|
def __init__(self, commandName="make", formatName="GNUMake"):
|
||
|
Builder.__init__(self, commandName=commandName, formatName=formatName)
|
||
|
|
||
|
|
||
|
class XcodeBuilder(Builder):
|
||
|
def __init__(self, commandName="xcodebuild", formatName="Xcode"):
|
||
|
Builder.__init__(self, commandName=commandName, formatName=formatName)
|
||
|
|
||
|
|
||
|
class AutoconfBuilder(GNUMakeBuilder):
|
||
|
def __init__(self, formatName="autoconf"):
|
||
|
GNUMakeBuilder.__init__(self, formatName=formatName)
|
||
|
|
||
|
def configure(self, dir=None, options=None):
|
||
|
#olddir = os.getcwd()
|
||
|
#os.chdir(dir)
|
||
|
|
||
|
configdir = dir
|
||
|
if not dir:
|
||
|
configdir = os.getcwd()
|
||
|
|
||
|
configure_cmd = ""
|
||
|
while os.path.exists(configdir):
|
||
|
config_cmd = os.path.join(configdir, "configure")
|
||
|
if not os.path.exists(config_cmd):
|
||
|
parentdir = os.path.abspath(os.path.join(configdir, ".."))
|
||
|
if configdir == parentdir:
|
||
|
break
|
||
|
|
||
|
configdir = parentdir
|
||
|
else:
|
||
|
configure_cmd = config_cmd
|
||
|
break
|
||
|
|
||
|
if not configure_cmd:
|
||
|
sys.stderr.write("Could not find configure script at %r. Have you run autoconf?\n" % dir)
|
||
|
return 1
|
||
|
|
||
|
optionsStr = " ".join(options) if options else ""
|
||
|
command = "%s %s" % (configure_cmd, optionsStr)
|
||
|
print(command)
|
||
|
result = os.system(command)
|
||
|
#os.chdir(olddir)
|
||
|
return result
|
||
|
|
||
|
|
||
|
class MSVCBuilder(Builder):
|
||
|
def __init__(self, commandName="nmake.exe"):
|
||
|
Builder.__init__(self, commandName=commandName, formatName="msvc")
|
||
|
|
||
|
def isAvailable(self):
|
||
|
PATH = os.environ['PATH'].split(os.path.pathsep)
|
||
|
for p in PATH:
|
||
|
if os.path.exists(os.path.join(p, self.name)):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def getProjectFileArg(self, projectFile = None):
|
||
|
result = []
|
||
|
if projectFile:
|
||
|
result.extend(['-f', projectFile])
|
||
|
|
||
|
return result
|
||
|
|
||
|
|
||
|
class MSVCProjectBuilder(Builder):
|
||
|
def __init__(self):
|
||
|
Builder.__init__(self, commandName="VCExpress.exe", formatName="msvcProject")
|
||
|
for key in ["VS90COMNTOOLS", "VC80COMNTOOLS", "VC71COMNTOOLS"]:
|
||
|
if os.environ.has_key(key):
|
||
|
self.programDir = os.path.join(os.environ[key], "..", "IDE")
|
||
|
|
||
|
if self.programDir == None:
|
||
|
for version in ["9.0", "8", ".NET 2003"]:
|
||
|
msvcDir = "C:\\Program Files\\Microsoft Visual Studio %s\\Common7\\IDE" % version
|
||
|
if os.path.exists(msvcDir):
|
||
|
self.programDir = msvcDir
|
||
|
|
||
|
def isAvailable(self):
|
||
|
if self.programDir:
|
||
|
path = os.path.join(self.programDir, self.name)
|
||
|
if os.path.exists(path):
|
||
|
return True
|
||
|
else:
|
||
|
# I don't have commercial versions of MSVC so I can't test this
|
||
|
name = "devenv.com"
|
||
|
path = os.path.join(self.programDir, name)
|
||
|
if os.path.exists(path):
|
||
|
self.name = "devenv.com"
|
||
|
return True
|
||
|
|
||
|
return False
|
||
|
|
||
|
builders = [GNUMakeBuilder, XcodeBuilder, AutoconfBuilder, MSVCBuilder, MSVCProjectBuilder]
|
||
|
|
||
|
def getAvailableBuilders():
|
||
|
availableBuilders = {}
|
||
|
for symbol in builders:
|
||
|
thisBuilder = symbol()
|
||
|
if thisBuilder.isAvailable():
|
||
|
availableBuilders[thisBuilder.formatName] = symbol
|
||
|
|
||
|
return availableBuilders
|