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