builder.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import os
  2. import subprocess
  3. import sys
  4. import time
  5. class BuildError(Exception):
  6. def __init__(self, value):
  7. self.value = value
  8. def __repr__(self):
  9. return repr(self.value)
  10. def runInDir(command, dir=None, verbose=True):
  11. if dir:
  12. olddir = os.getcwd()
  13. os.chdir(dir)
  14. commandStr = " ".join(command)
  15. if verbose:
  16. print(commandStr)
  17. result = os.system(commandStr)
  18. if dir:
  19. os.chdir(olddir)
  20. return result
  21. class Builder:
  22. """
  23. Base class exposing the Builder interface.
  24. """
  25. def __init__(self, formatName="", commandName="", programDir=None):
  26. """
  27. formatName = human readable name for project format (should correspond with Bakefile names)
  28. commandName = name of command line program used to invoke builder
  29. programDir = directory program is located in, if not on the path
  30. """
  31. self.dir = dir
  32. self.name = commandName
  33. self.formatName = formatName
  34. self.programDir = programDir
  35. self.doSetup()
  36. def doSetup(self):
  37. """
  38. Do anything special needed to configure the environment to build with this builder.
  39. """
  40. pass
  41. def isAvailable(self):
  42. """
  43. Run sanity checks before attempting to build with this format
  44. """
  45. # Make sure the builder program exists
  46. programPath = self.getProgramPath()
  47. if os.path.exists(programPath):
  48. return True
  49. else:
  50. # check the PATH for the program
  51. # TODO: How do we check if we're in Cygwin?
  52. if sys.platform.startswith("win"):
  53. result = os.system(self.name)
  54. if result == 0:
  55. return True
  56. dirs = os.environ["PATH"].split(":")
  57. for dir in dirs:
  58. if os.path.isfile(os.path.join(dir, self.name)):
  59. return True
  60. else:
  61. result = os.system("which %s" % self.name)
  62. if result == 0:
  63. return True
  64. return False
  65. def getProgramPath(self):
  66. if self.programDir:
  67. path = os.path.join(self.programDir, self.name)
  68. if sys.platform.startswith("win"):
  69. path = '"%s"' % path
  70. return path
  71. return self.name
  72. def getProjectFileArg(self, projectFile = None):
  73. result = []
  74. if projectFile:
  75. result.append(projectFile)
  76. return result
  77. def clean(self, dir=None, projectFile=None, options=[]):
  78. """
  79. dir = the directory containing the project file
  80. projectFile = Some formats need to explicitly specify the project file's name
  81. """
  82. if self.isAvailable():
  83. args = [self.getProgramPath()]
  84. pfArg = self.getProjectFileArg(projectFile)
  85. if pfArg:
  86. args.extend(pfArg)
  87. args.append("clean")
  88. if options:
  89. args.extend(options)
  90. result = runInDir(args, dir)
  91. return result
  92. return False
  93. def configure(self, dir=None, options=[]):
  94. # if we don't have configure, just report success
  95. return 0
  96. def build(self, dir=None, projectFile=None, targets=None, options=[]):
  97. if self.isAvailable():
  98. args = [self.getProgramPath()]
  99. pfArg = self.getProjectFileArg(projectFile)
  100. if pfArg:
  101. args.extend(pfArg)
  102. # Important Note: if extending args, check it first!
  103. # NoneTypes are not iterable and will crash the clean, build, or install!
  104. # Very very irritating when this happens right at the end.
  105. if options:
  106. args.extend(options)
  107. result = runInDir(args, dir)
  108. return result
  109. return 1
  110. def install(self, dir=None, projectFile=None, options=[]):
  111. if self.isAvailable():
  112. args = [self.getProgramPath()]
  113. pfArg = self.getProjectFileArg(projectFile)
  114. if pfArg:
  115. args.extend(pfArg)
  116. args.append("install")
  117. if options:
  118. args.extend(options)
  119. result = runInDir(args, dir)
  120. return result
  121. return 1
  122. # Concrete subclasses of abstract Builder interface
  123. class GNUMakeBuilder(Builder):
  124. def __init__(self, commandName="make", formatName="GNUMake"):
  125. Builder.__init__(self, commandName=commandName, formatName=formatName)
  126. class XcodeBuilder(Builder):
  127. def __init__(self, commandName="xcodebuild", formatName="Xcode"):
  128. Builder.__init__(self, commandName=commandName, formatName=formatName)
  129. class AutoconfBuilder(GNUMakeBuilder):
  130. def __init__(self, formatName="autoconf"):
  131. GNUMakeBuilder.__init__(self, formatName=formatName)
  132. def configure(self, dir=None, options=None):
  133. #olddir = os.getcwd()
  134. #os.chdir(dir)
  135. configdir = dir
  136. if not dir:
  137. configdir = os.getcwd()
  138. configure_cmd = ""
  139. while os.path.exists(configdir):
  140. config_cmd = os.path.join(configdir, "configure")
  141. if not os.path.exists(config_cmd):
  142. parentdir = os.path.abspath(os.path.join(configdir, ".."))
  143. if configdir == parentdir:
  144. break
  145. configdir = parentdir
  146. else:
  147. configure_cmd = config_cmd
  148. break
  149. if not configure_cmd:
  150. sys.stderr.write("Could not find configure script at %r. Have you run autoconf?\n" % dir)
  151. return 1
  152. optionsStr = " ".join(options) if options else ""
  153. command = "%s %s" % (configure_cmd, optionsStr)
  154. print(command)
  155. result = os.system(command)
  156. #os.chdir(olddir)
  157. return result
  158. class MSVCBuilder(Builder):
  159. def __init__(self, commandName="nmake.exe"):
  160. Builder.__init__(self, commandName=commandName, formatName="msvc")
  161. def isAvailable(self):
  162. PATH = os.environ['PATH'].split(os.path.pathsep)
  163. for p in PATH:
  164. if os.path.exists(os.path.join(p, self.name)):
  165. return True
  166. return False
  167. def getProjectFileArg(self, projectFile = None):
  168. result = []
  169. if projectFile:
  170. result.extend(['-f', projectFile])
  171. return result
  172. class MSVCProjectBuilder(Builder):
  173. def __init__(self):
  174. Builder.__init__(self, commandName="VCExpress.exe", formatName="msvcProject")
  175. for key in ["VS90COMNTOOLS", "VC80COMNTOOLS", "VC71COMNTOOLS"]:
  176. if os.environ.has_key(key):
  177. self.programDir = os.path.join(os.environ[key], "..", "IDE")
  178. if self.programDir == None:
  179. for version in ["9.0", "8", ".NET 2003"]:
  180. msvcDir = "C:\\Program Files\\Microsoft Visual Studio %s\\Common7\\IDE" % version
  181. if os.path.exists(msvcDir):
  182. self.programDir = msvcDir
  183. def isAvailable(self):
  184. if self.programDir:
  185. path = os.path.join(self.programDir, self.name)
  186. if os.path.exists(path):
  187. return True
  188. else:
  189. # I don't have commercial versions of MSVC so I can't test this
  190. name = "devenv.com"
  191. path = os.path.join(self.programDir, name)
  192. if os.path.exists(path):
  193. self.name = "devenv.com"
  194. return True
  195. return False
  196. builders = [GNUMakeBuilder, XcodeBuilder, AutoconfBuilder, MSVCBuilder, MSVCProjectBuilder]
  197. def getAvailableBuilders():
  198. availableBuilders = {}
  199. for symbol in builders:
  200. thisBuilder = symbol()
  201. if thisBuilder.isAvailable():
  202. availableBuilders[thisBuilder.formatName] = symbol
  203. return availableBuilders