]>
Commit | Line | Data |
---|---|---|
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=None): | |
157 | #olddir = os.getcwd() | |
158 | #os.chdir(dir) | |
159 | ||
160 | configdir = dir | |
161 | if not dir: | |
162 | configdir = os.getcwd() | |
163 | ||
164 | configure_cmd = "" | |
165 | while os.path.exists(configdir): | |
166 | config_cmd = os.path.join(configdir, "configure") | |
167 | if not os.path.exists(config_cmd): | |
168 | parentdir = os.path.abspath(os.path.join(configdir, "..")) | |
169 | if configdir == parentdir: | |
170 | break | |
171 | ||
172 | configdir = parentdir | |
173 | else: | |
174 | configure_cmd = config_cmd | |
175 | break | |
176 | ||
177 | if not configure_cmd: | |
178 | sys.stderr.write("Could not find configure script at %r. Have you run autoconf?\n" % dir) | |
179 | return 1 | |
180 | ||
181 | optionsStr = " ".join(options) if options else "" | |
182 | command = "%s %s" % (configure_cmd, 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 |