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