]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/env python | |
2 | ||
3 | import sys | |
4 | import os | |
5 | import platform | |
6 | import getopt | |
7 | ||
8 | LOG = None | |
9 | VERBOSE = False | |
10 | REQUIRE_ALL = False | |
11 | ||
12 | # standard line print | |
13 | def lprint(x): | |
14 | print x | |
15 | LOG.write(x + "\n") | |
16 | ||
17 | # verbose print | |
18 | def vprint(x=""): | |
19 | if VERBOSE: | |
20 | print x | |
21 | LOG.write(x + "\n") | |
22 | ||
23 | # immediate (no newline) print | |
24 | def iprint(x): | |
25 | sys.stdout.write(x) | |
26 | sys.stdout.flush() | |
27 | LOG.write(x) | |
28 | ||
29 | class IniParser: | |
30 | def __init__(self, file): | |
31 | self.__d = {} | |
32 | sectiondata = None | |
33 | for x in file.readlines(): | |
34 | x = x.replace("\r\n", "").replace("\n", "") | |
35 | xs = x.strip() | |
36 | if xs == "" or xs.startswith("#"): | |
37 | continue | |
38 | ||
39 | if x.startswith("[") and x.endswith("]"): | |
40 | sectiondata = {} | |
41 | keydata = [] | |
42 | self.__d[x[1:-1]] = (sectiondata, keydata) | |
43 | continue | |
44 | ||
45 | i = x.index("=") | |
46 | key = x[:i] | |
47 | sectiondata[key] = x[i+1:] | |
48 | keydata.append((key, sectiondata[key])) | |
49 | ||
50 | def has_key(self, key): | |
51 | return self.__d.has_key(key) | |
52 | ||
53 | def setdefault(self, key, value=None): | |
54 | if self.has_key(key): | |
55 | return self[key] | |
56 | return value | |
57 | ||
58 | def __getitem__(self, key): | |
59 | return self.__d[key][0] | |
60 | ||
61 | def keys(self, key): | |
62 | return self.__d[key][1] | |
63 | ||
64 | class ConfigureIniParser(IniParser): | |
65 | def __init__(self, file): | |
66 | IniParser.__init__(self, file) | |
67 | ||
68 | self.modules = {} | |
69 | self.buildorder = [] | |
70 | self.updatemodules(self.keys("modules")) | |
71 | ||
72 | self.selectlibs = {} | |
73 | for k, v in self.setdefault("selectlibs", {}).items(): | |
74 | self.selectlibs[k] = v.split() | |
75 | ||
76 | libs = self.setdefault("core", {"libs": ""})["libs"].split() | |
77 | libs = [(x, self["lib%s" % x]) for x in libs] | |
78 | for k, v in libs: | |
79 | v.setdefault("libname", k) | |
80 | v.setdefault("format", "lib%s.so") | |
81 | v.setdefault("headerpath", None) | |
82 | v.setdefault("additionaldirs", None) | |
83 | v.setdefault("alwayspresent", None) | |
84 | ||
85 | if v["additionaldirs"]: | |
86 | v["additionaldirs"] = v["additionaldirs"].split() | |
87 | v["libname"] = v["libname"].split() | |
88 | ||
89 | self.libs = dict(libs) | |
90 | self.searchincludes = self["search"]["include"].split() | |
91 | self.searchlibs = self["search"]["lib"].split() | |
92 | self.makes = {} | |
93 | for k, v in self.setdefault("makes", {}).items(): | |
94 | self.makes[k] = v | |
95 | ||
96 | self.osflags = {} | |
97 | if self.has_key("osvars"): | |
98 | for k, v in self.keys("osvars"): | |
99 | self.osflags.setdefault(k, []).append(v) | |
100 | ||
101 | self.subs = [("-%s-" % k, v) for k, v in self.setdefault("subs", {}).items()] | |
102 | self.options = self["options"] | |
103 | ||
104 | def configprint(self): | |
105 | vprint("--- config --------------------------------------------") | |
106 | for x in dir(self): | |
107 | if x.startswith("_"): | |
108 | continue | |
109 | v = getattr(self, x) | |
110 | if not isinstance(v, list) and not isinstance(v, dict): | |
111 | continue | |
112 | vprint("%-50s: %s" % (`x`, `v`)) | |
113 | vprint("--- config --------------------------------------------") | |
114 | ||
115 | def updatemodules(self, x, workspace = None): | |
116 | for k, v in x: | |
117 | if workspace and workspace != ".": | |
118 | name = workspace + "/" + k | |
119 | else: | |
120 | name = k | |
121 | self.buildorder.append(name) | |
122 | self.modules[name] = v.split() | |
123 | ||
124 | class MultiConfigureIniParser(ConfigureIniParser): | |
125 | def __init__(self, files): | |
126 | ConfigureIniParser.__init__(self, files[0][1]) | |
127 | ||
128 | for workspace, file in files[1:]: | |
129 | c2 = IniParser(file) | |
130 | if c2.has_key("modules"): | |
131 | self.updatemodules(c2.keys("modules"), workspace) | |
132 | ||
133 | if c2.has_key("search"): | |
134 | if c2["search"].has_key("include"): | |
135 | self.searchincludes = self.searchincludes + c2["search"]["include"].split() | |
136 | if c2["search"].has_key("lib"): | |
137 | self.searchlibs = self.searchlibs + c2["search"]["lib"].split() | |
138 | ||
139 | if c2.has_key("options"): | |
140 | self.options.update(c2["options"]) | |
141 | ||
142 | if c2.has_key("osvars"): | |
143 | for k, v in c2.keys("osvars"): | |
144 | self.osflags.setdefault(k, []).append(v) | |
145 | ||
146 | def librarycheck(libraries, includes, libs): | |
147 | def findlibrarypaths(library, x, includes, libs): | |
148 | if x["alwayspresent"]: | |
149 | return True | |
150 | def locate(needle, haystack): | |
151 | vprint() | |
152 | vprint("searching for %s in: %s" % (needle, haystack)) | |
153 | ||
154 | for x in haystack: | |
155 | p = os.path.join(x, needle) | |
156 | if os.path.exists(p): | |
157 | vprint(" found: %s" % p) | |
158 | return x | |
159 | ||
160 | found = [] | |
161 | ||
162 | def mergepaths(a, b): | |
163 | ret = list(a[:]) | |
164 | for x in a: | |
165 | for y in b: | |
166 | ret.append(os.path.join(x, y)) | |
167 | return ret | |
168 | ||
169 | libname = x["libname"] | |
170 | ||
171 | searchdir = mergepaths(includes, libname) | |
172 | incpath = locate(x["include"], searchdir) | |
173 | if not incpath: | |
174 | return | |
175 | ||
176 | if x["headerpath"]: | |
177 | incpath = os.path.abspath(os.path.join(incpath, x["headerpath"])) | |
178 | ||
179 | searchdir = mergepaths(libs, libname) | |
180 | if x["additionaldirs"]: | |
181 | searchdir = mergepaths(searchdir, [""] + x["additionaldirs"]) | |
182 | ||
183 | for l in x.has_key("soname") and [x["soname"]] or libname: | |
184 | libpath = locate(x["format"] % l, searchdir) | |
185 | if libpath: | |
186 | return libpath, l, incpath | |
187 | ||
188 | libsfound = [] | |
189 | output = [] | |
190 | ||
191 | for k in libraries.keys(): | |
192 | iprint("checking for %s... " % k) | |
193 | ret = findlibrarypaths(k, libraries[k], includes, libs) | |
194 | if not ret: | |
195 | lprint("failed") | |
196 | continue | |
197 | ||
198 | libsfound.append(k) | |
199 | ||
200 | if ret is not True: | |
201 | libpath, libname, incpath = ret | |
202 | uk = k.upper() | |
203 | ||
204 | x = libraries[k] | |
205 | libline = "LIB%s=-L%s -l%s" % (uk, libpath, libname) | |
206 | incline = "INC%s=-I%s" % (uk, incpath) | |
207 | output.append(libline) | |
208 | output.append(incline) | |
209 | ||
210 | lprint("ok") | |
211 | if ret is not True: | |
212 | vprint("library path: %s" % libline) | |
213 | vprint("include path: %s" % incline) | |
214 | ||
215 | return output, libsfound | |
216 | ||
217 | def systemcheck(makes, osflags): | |
218 | output = [] | |
219 | ||
220 | iprint("checking for system... ") | |
221 | system = platform.system() | |
222 | lprint(system) | |
223 | ||
224 | iprint("checking for make version... ") | |
225 | make = makes.setdefault(system, "gmake") | |
226 | lprint(make) | |
227 | ||
228 | for v in osflags.setdefault(system, []): | |
229 | output.append(v) | |
230 | return output, make | |
231 | ||
232 | def modulecheck(modules, libsfound, buildorder, selectlibs, overrides): | |
233 | defaultselections = {} | |
234 | ||
235 | for k, v in selectlibs.items(): | |
236 | if overrides.has_key(k): | |
237 | assert overrides[k] in libsfound | |
238 | libsfound.append(k) | |
239 | defaultselections[k] = overrides[k] | |
240 | else: | |
241 | for x in v: | |
242 | if x in libsfound: | |
243 | libsfound.append(k) | |
244 | defaultselections[k] = x | |
245 | break | |
246 | ||
247 | building = set() | |
248 | for k, v in modules.items(): | |
249 | for x in v: | |
250 | if x not in libsfound: | |
251 | break | |
252 | else: | |
253 | building.add(k) | |
254 | ||
255 | notfound = set(filter(lambda x: not os.path.exists(x), building)) | |
256 | ||
257 | cantbuild = set(modules) - building | |
258 | building = building - notfound | |
259 | ||
260 | orderedbuild = [] | |
261 | for x in buildorder: | |
262 | if x in building: | |
263 | orderedbuild.append(x) | |
264 | ||
265 | build = ["DIRS=%s" % (" ".join(orderedbuild))] | |
266 | return build, orderedbuild, notfound, cantbuild, defaultselections | |
267 | ||
268 | def writemakefile(inc, out, appendstart=None, appendend=None, silent=False): | |
269 | p = open(out, "w") | |
270 | p.write("## AUTOMATICALLY GENERATED -- EDIT %s INSTEAD\n\n" % inc) | |
271 | if appendstart: | |
272 | p.write("\n".join(appendstart)) | |
273 | p.write("\n") | |
274 | ||
275 | f = open(inc, "r") | |
276 | try: | |
277 | for l in f.readlines(): | |
278 | p.write(l) | |
279 | finally: | |
280 | f.close() | |
281 | ||
282 | if appendend: | |
283 | p.write("\n".join(appendend)) | |
284 | p.write("\n") | |
285 | ||
286 | p.close() | |
287 | if not silent: | |
288 | lprint("configure: wrote %s" % out) | |
289 | ||
290 | def writeconfigh(file, modules, defaultselections): | |
291 | f = open(file, "w") | |
292 | f.write("/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n") | |
293 | ||
294 | for x in modules: | |
295 | f.write("#define HAVE_%s\n" % x.upper()) | |
296 | for k, v in defaultselections.items(): | |
297 | f.write("#define USE_%s_%s\n" % (k.upper(), v.upper())) | |
298 | f.close() | |
299 | ||
300 | lprint("configure: wrote %s" % file) | |
301 | ||
302 | def configure(config, selectoverrides, workspaces): | |
303 | # figure out make and any custom OS flags | |
304 | flags, make = systemcheck(config.makes, config.osflags) | |
305 | ||
306 | # find the libraries/includes we have and their paths | |
307 | f, libsfound = librarycheck(config.libs, config.searchincludes, config.searchlibs) | |
308 | for k, v in selectoverrides.items(): | |
309 | if not v in libsfound: | |
310 | lprint("configure: can't set %s to %s as %s was not found." % (k, v, v)) | |
311 | return | |
312 | ||
313 | flags = flags + f | |
314 | ||
315 | # see which modules we can build | |
316 | buildlines, building, notfound, cantbuild, defaultselections = modulecheck(config.modules, libsfound, config.buildorder, config.selectlibs, selectoverrides) | |
317 | ||
318 | for k, v in defaultselections.items(): | |
319 | lprint("configure: selected %s as %s" % (v, k)) | |
320 | flags.append("LIB%s=$(LIB%s)" % (k.upper(), v.upper())) | |
321 | flags.append("INC%s=$(INC%s)" % (k.upper(), v.upper())) | |
322 | ||
323 | writemakefile("build.mk.in", "build.mk", appendend=flags + ["=".join(x) for x in config.options.items()] + ["DIRS=" + " ".join(building), "WORKSPACES=" + " ".join(workspaces)]) | |
324 | ||
325 | writeconfigh("config.h", libsfound, defaultselections) | |
326 | ||
327 | lprint("configure: selected: %s" % " ".join(building)) | |
328 | if len(notfound) > 0: | |
329 | lprint("configure: couldn't find: %s" % " ".join(notfound)) | |
330 | check_require_all() | |
331 | ||
332 | if len(cantbuild) > 0: | |
333 | lprint("configure: can't select: %s" % " ".join(cantbuild)) | |
334 | check_require_all() | |
335 | ||
336 | def check_require_all(): | |
337 | if REQUIRE_ALL: | |
338 | lprint("configure: require-all selected, so failing") | |
339 | sys.exit(1) | |
340 | ||
341 | validopts = {} | |
342 | ||
343 | def usage(): | |
344 | ||
345 | print " Usage: %s [-h] [-v] [options]" % sys.argv[0] | |
346 | ||
347 | print " Additional options are:" | |
348 | for k, v in validopts.items(): | |
349 | print " --with-%s=[%s]" % (v[0], "|".join(v[1])) | |
350 | print " -L [additional lib dir]" | |
351 | print " -I [additional include dir]" | |
352 | print " -m [additional module]" | |
353 | print " -R: require everything" | |
354 | print " -v: verbose" | |
355 | ||
356 | def main(): | |
357 | global LOG, VERBOSE, REQUIRE_ALL | |
358 | ||
359 | files, workspaces = [], [] | |
360 | for root, _, file_list in os.walk("."): | |
361 | if "configure.ini" not in file_list: | |
362 | continue | |
363 | ||
364 | print "found workspace: %s" % root | |
365 | workspaces.append(root) | |
366 | ||
367 | path = os.path.join(root, "configure.ini") | |
368 | files.append( (root, open(path, "r")) ) | |
369 | ||
370 | local_path = os.path.join(root, "configure.ini.local") | |
371 | if os.path.exists(local_path): | |
372 | files.append( (root, open(local_path, "r")) ) | |
373 | ||
374 | config = MultiConfigureIniParser(files) | |
375 | ||
376 | mopts = [] | |
377 | ||
378 | for k, v in config.selectlibs.items(): | |
379 | mopts.append("with-%s=" % k) | |
380 | validopts["--with-%s" % k] = (k, v) | |
381 | ||
382 | try: | |
383 | opts, args = getopt.getopt(sys.argv[1:], "hvcI:L:m:R", mopts) | |
384 | except getopt.GetoptError, err: | |
385 | print str(err) | |
386 | usage() | |
387 | return | |
388 | ||
389 | overrides = {} | |
390 | libs = [] | |
391 | includes = [] | |
392 | modules = [] | |
393 | ||
394 | for o, a in opts: | |
395 | if validopts.has_key(o): | |
396 | v = validopts[o] | |
397 | if not a in v[1]: | |
398 | usage() | |
399 | return | |
400 | overrides[v[0]] = a | |
401 | elif o == "-h": | |
402 | usage() | |
403 | return | |
404 | elif o == "-v": | |
405 | VERBOSE = True | |
406 | elif o == "-R": | |
407 | REQUIRE_ALL = True | |
408 | elif o == "-L": | |
409 | libs.append(a) | |
410 | elif o == "-I": | |
411 | includes.append(a) | |
412 | elif o == "-m": | |
413 | modules.append(a) | |
414 | else: | |
415 | assert False, "bad option" | |
416 | ||
417 | LOG = open(".configure.log", "w") | |
418 | vprint("invoked as: %r" % sys.argv) | |
419 | config.updatemodules([(x, "") for x in modules]) | |
420 | config.searchlibs.extend(libs) | |
421 | config.searchincludes.extend(includes) | |
422 | config.configprint() | |
423 | ||
424 | configure(config, overrides, workspaces) | |
425 | ||
426 | if __name__ == "__main__": | |
427 | main() |