s3: libsmbclient: Add missing talloc stackframe.
[samba.git] / third_party / waf / wafadmin / Configure.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2008 (ita)
4
5 """
6 Configuration system
7
8 A configuration instance is created when "waf configure" is called, it is used to:
9 * create data dictionaries (Environment instances)
10 * store the list of modules to import
11
12 The old model (copied from Scons) was to store logic (mapping file extensions to functions)
13 along with the data. In Waf a way was found to separate that logic by adding an indirection
14 layer (storing the names in the Environment instances)
15
16 In the new model, the logic is more object-oriented, and the user scripts provide the
17 logic. The data files (Environments) must contain configuration data only (flags, ..).
18
19 Note: the c/c++ related code is in the module config_c
20 """
21
22 import os, shlex, sys, time
23 try: import cPickle
24 except ImportError: import pickle as cPickle
25 import Environment, Utils, Options, Logs
26 from Logs import warn
27 from Constants import *
28
29 try:
30         from urllib import request
31 except:
32         from urllib import urlopen
33 else:
34         urlopen = request.urlopen
35
36 conf_template = '''# project %(app)s configured on %(now)s by
37 # waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s)
38 # using %(args)s
39 #
40 '''
41
42 class ConfigurationError(Utils.WscriptError):
43         pass
44
45 autoconfig = False
46 "reconfigure the project automatically"
47
48 def find_file(filename, path_list):
49         """find a file in a list of paths
50         @param filename: name of the file to search for
51         @param path_list: list of directories to search
52         @return: the first occurrence filename or '' if filename could not be found
53 """
54         for directory in Utils.to_list(path_list):
55                 if os.path.exists(os.path.join(directory, filename)):
56                         return directory
57         return ''
58
59 def find_program_impl(env, filename, path_list=[], var=None, environ=None):
60         """find a program in folders path_lst, and sets env[var]
61         @param env: environment
62         @param filename: name of the program to search for
63         @param path_list: list of directories to search for filename
64         @param var: environment value to be checked for in env or os.environ
65         @return: either the value that is referenced with [var] in env or os.environ
66          or the first occurrence filename or '' if filename could not be found
67 """
68
69         if not environ:
70                 environ = os.environ
71
72         try: path_list = path_list.split()
73         except AttributeError: pass
74
75         if var:
76                 if env[var]: return env[var]
77                 if var in environ: env[var] = environ[var]
78
79         if not path_list: path_list = environ.get('PATH', '').split(os.pathsep)
80
81         ext = (Options.platform == 'win32') and '.exe,.com,.bat,.cmd' or ''
82         for y in [filename+x for x in ext.split(',')]:
83                 for directory in path_list:
84                         x = os.path.join(directory, y)
85                         if os.path.isfile(x):
86                                 if var: env[var] = x
87                                 return x
88         return ''
89
90 class ConfigurationContext(Utils.Context):
91         tests = {}
92         error_handlers = []
93         def __init__(self, env=None, blddir='', srcdir=''):
94                 self.env = None
95                 self.envname = ''
96
97                 self.environ = dict(os.environ)
98
99                 self.line_just = 40
100
101                 self.blddir = blddir
102                 self.srcdir = srcdir
103                 self.all_envs = {}
104
105                 # curdir: necessary for recursion
106                 self.cwd = self.curdir = os.getcwd()
107
108                 self.tools = [] # tools loaded in the configuration, and that will be loaded when building
109
110                 self.setenv(DEFAULT)
111
112                 self.lastprog = ''
113
114                 self.hash = 0
115                 self.files = []
116
117                 self.tool_cache = []
118
119                 if self.blddir:
120                         self.post_init()
121
122         def post_init(self):
123
124                 self.cachedir = os.path.join(self.blddir, CACHE_DIR)
125
126                 path = os.path.join(self.blddir, WAF_CONFIG_LOG)
127                 try: os.unlink(path)
128                 except (OSError, IOError): pass
129
130                 try:
131                         self.log = open(path, 'w')
132                 except (OSError, IOError):
133                         self.fatal('could not open %r for writing' % path)
134
135                 app = Utils.g_module.APPNAME
136                 if app:
137                         ver = getattr(Utils.g_module, 'VERSION', '')
138                         if ver:
139                                 app = "%s (%s)" % (app, ver)
140
141                 now = time.ctime()
142                 pyver = sys.hexversion
143                 systype = sys.platform
144                 args = " ".join(sys.argv)
145                 wafver = WAFVERSION
146                 abi = ABI
147                 self.log.write(conf_template % vars())
148
149         def __del__(self):
150                 """cleanup function: close config.log"""
151
152                 # may be ran by the gc, not always after initialization
153                 if hasattr(self, 'log') and self.log:
154                         self.log.close()
155
156         def fatal(self, msg):
157                 raise ConfigurationError(msg)
158
159         def check_tool(self, input, tooldir=None, funs=None):
160                 "load a waf tool"
161
162                 tools = Utils.to_list(input)
163                 if tooldir: tooldir = Utils.to_list(tooldir)
164                 for tool in tools:
165                         tool = tool.replace('++', 'xx')
166                         if tool == 'java': tool = 'javaw'
167                         if tool.lower() == 'unittest': tool = 'unittestw'
168                         # avoid loading the same tool more than once with the same functions
169                         # used by composite projects
170
171                         mag = (tool, id(self.env), funs)
172                         if mag in self.tool_cache:
173                                 continue
174                         self.tool_cache.append(mag)
175
176                         module = None
177                         try:
178                                 module = Utils.load_tool(tool, tooldir)
179                         except Exception, e:
180                                 ex = e
181                                 if Options.options.download:
182                                         _3rdparty = os.path.normpath(Options.tooldir[0] + os.sep + '..' + os.sep + '3rdparty')
183
184                                         # try to download the tool from the repository then
185                                         # the default is set to false
186                                         for x in Utils.to_list(Options.remote_repo):
187                                                 for sub in ['branches/waf-%s/wafadmin/3rdparty' % WAFVERSION, 'trunk/wafadmin/3rdparty']:
188                                                         url = '/'.join((x, sub, tool + '.py'))
189                                                         try:
190                                                                 web = urlopen(url)
191                                                                 if web.getcode() != 200:
192                                                                         continue
193                                                         except Exception, e:
194                                                                 # on python3 urlopen throws an exception
195                                                                 continue
196                                                         else:
197                                                                 loc = None
198                                                                 try:
199                                                                         loc = open(_3rdparty + os.sep + tool + '.py', 'wb')
200                                                                         loc.write(web.read())
201                                                                         web.close()
202                                                                 finally:
203                                                                         if loc:
204                                                                                 loc.close()
205                                                                 Logs.warn('downloaded %s from %s' % (tool, url))
206                                                                 try:
207                                                                         module = Utils.load_tool(tool, tooldir)
208                                                                 except:
209                                                                         Logs.warn('module %s from %s is unusable' % (tool, url))
210                                                                         try:
211                                                                                 os.unlink(_3rdparty + os.sep + tool + '.py')
212                                                                         except:
213                                                                                 pass
214                                                                         continue
215                                                 else:
216                                                         break
217
218                                         if not module:
219                                                 Logs.error('Could not load the tool %r or download a suitable replacement from the repository (sys.path %r)\n%s' % (tool, sys.path, e))
220                                                 raise ex
221                                 else:
222                                         Logs.error('Could not load the tool %r in %r (try the --download option?):\n%s' % (tool, sys.path, e))
223                                         raise ex
224
225                         if funs is not None:
226                                 self.eval_rules(funs)
227                         else:
228                                 func = getattr(module, 'detect', None)
229                                 if func:
230                                         if type(func) is type(find_file): func(self)
231                                         else: self.eval_rules(func)
232
233                         self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs})
234
235         def sub_config(self, k):
236                 "executes the configure function of a wscript module"
237                 self.recurse(k, name='configure')
238
239         def pre_recurse(self, name_or_mod, path, nexdir):
240                 return {'conf': self, 'ctx': self}
241
242         def post_recurse(self, name_or_mod, path, nexdir):
243                 if not autoconfig:
244                         return
245                 self.hash = hash((self.hash, getattr(name_or_mod, 'waf_hash_val', name_or_mod)))
246                 self.files.append(path)
247
248         def store(self, file=''):
249                 "save the config results into the cache file"
250                 if not os.path.isdir(self.cachedir):
251                         os.makedirs(self.cachedir)
252
253                 if not file:
254                         file = open(os.path.join(self.cachedir, 'build.config.py'), 'w')
255                 file.write('version = 0x%x\n' % HEXVERSION)
256                 file.write('tools = %r\n' % self.tools)
257                 file.close()
258
259                 if not self.all_envs:
260                         self.fatal('nothing to store in the configuration context!')
261                 for key in self.all_envs:
262                         tmpenv = self.all_envs[key]
263                         tmpenv.store(os.path.join(self.cachedir, key + CACHE_SUFFIX))
264
265         def set_env_name(self, name, env):
266                 "add a new environment called name"
267                 self.all_envs[name] = env
268                 return env
269
270         def retrieve(self, name, fromenv=None):
271                 "retrieve an environment called name"
272                 try:
273                         env = self.all_envs[name]
274                 except KeyError:
275                         env = Environment.Environment()
276                         env['PREFIX'] = os.path.abspath(os.path.expanduser(Options.options.prefix))
277                         self.all_envs[name] = env
278                 else:
279                         if fromenv: warn("The environment %s may have been configured already" % name)
280                 return env
281
282         def setenv(self, name):
283                 "enable the environment called name"
284                 self.env = self.retrieve(name)
285                 self.envname = name
286
287         def add_os_flags(self, var, dest=None):
288                 # do not use 'get' to make certain the variable is not defined
289                 try: self.env.append_value(dest or var, Utils.to_list(self.environ[var]))
290                 except KeyError: pass
291
292         def check_message_1(self, sr):
293                 self.line_just = max(self.line_just, len(sr))
294                 for x in ('\n', self.line_just * '-', '\n', sr, '\n'):
295                         self.log.write(x)
296                 Utils.pprint('NORMAL', "%s :" % sr.ljust(self.line_just), sep='')
297
298         def check_message_2(self, sr, color='GREEN'):
299                 self.log.write(sr)
300                 self.log.write('\n')
301                 Utils.pprint(color, sr)
302
303         def check_message(self, th, msg, state, option=''):
304                 sr = 'Checking for %s %s' % (th, msg)
305                 self.check_message_1(sr)
306                 p = self.check_message_2
307                 if state: p('ok ' + str(option))
308                 else: p('not found', 'YELLOW')
309
310         # FIXME remove in waf 1.6
311         # the parameter 'option' is not used (kept for compatibility)
312         def check_message_custom(self, th, msg, custom, option='', color='PINK'):
313                 sr = 'Checking for %s %s' % (th, msg)
314                 self.check_message_1(sr)
315                 self.check_message_2(custom, color)
316
317         def msg(self, msg, result, color=None):
318                 """Prints a configuration message 'Checking for xxx: ok'"""
319                 self.start_msg('Checking for ' + msg)
320
321                 if not isinstance(color, str):
322                         color = result and 'GREEN' or 'YELLOW'
323
324                 self.end_msg(result, color)
325
326         def start_msg(self, msg):
327                 try:
328                         if self.in_msg:
329                                 return
330                 except:
331                         self.in_msg = 0
332                 self.in_msg += 1
333
334                 self.line_just = max(self.line_just, len(msg))
335                 for x in ('\n', self.line_just * '-', '\n', msg, '\n'):
336                         self.log.write(x)
337                 Utils.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='')
338
339         def end_msg(self, result, color):
340                 self.in_msg -= 1
341                 if self.in_msg:
342                         return
343
344                 if not color:
345                         color = 'GREEN'
346                 if result == True:
347                         msg = 'ok'
348                 elif result == False:
349                         msg = 'not found'
350                         color = 'YELLOW'
351                 else:
352                         msg = str(result)
353
354                 self.log.write(msg)
355                 self.log.write('\n')
356                 Utils.pprint(color, msg)
357
358         def find_program(self, filename, path_list=[], var=None, mandatory=False):
359                 "wrapper that adds a configuration message"
360
361                 ret = None
362                 if var:
363                         if self.env[var]:
364                                 ret = self.env[var]
365                         elif var in os.environ:
366                                 ret = os.environ[var]
367
368                 if not isinstance(filename, list): filename = [filename]
369                 if not ret:
370                         for x in filename:
371                                 ret = find_program_impl(self.env, x, path_list, var, environ=self.environ)
372                                 if ret: break
373
374                 self.check_message_1('Checking for program %s' % ' or '.join(filename))
375                 self.log.write('  find program=%r paths=%r var=%r\n  -> %r\n' % (filename, path_list, var, ret))
376                 if ret:
377                         Utils.pprint('GREEN', str(ret))
378                 else:
379                         Utils.pprint('YELLOW', 'not found')
380                         if mandatory:
381                                 self.fatal('The program %r is required' % filename)
382
383                 if var:
384                         self.env[var] = ret
385                 return ret
386
387         def cmd_to_list(self, cmd):
388                 "commands may be written in pseudo shell like 'ccache g++'"
389                 if isinstance(cmd, str) and cmd.find(' '):
390                         try:
391                                 os.stat(cmd)
392                         except OSError:
393                                 return shlex.split(cmd)
394                         else:
395                                 return [cmd]
396                 return cmd
397
398         def __getattr__(self, name):
399                 r = self.__class__.__dict__.get(name, None)
400                 if r: return r
401                 if name and name.startswith('require_'):
402
403                         for k in ['check_', 'find_']:
404                                 n = name.replace('require_', k)
405                                 ret = self.__class__.__dict__.get(n, None)
406                                 if ret:
407                                         def run(*k, **kw):
408                                                 r = ret(self, *k, **kw)
409                                                 if not r:
410                                                         self.fatal('requirement failure')
411                                                 return r
412                                         return run
413                 self.fatal('No such method %r' % name)
414
415         def eval_rules(self, rules):
416                 self.rules = Utils.to_list(rules)
417                 for x in self.rules:
418                         f = getattr(self, x)
419                         if not f: self.fatal("No such method '%s'." % x)
420                         try:
421                                 f()
422                         except Exception, e:
423                                 ret = self.err_handler(x, e)
424                                 if ret == BREAK:
425                                         break
426                                 elif ret == CONTINUE:
427                                         continue
428                                 else:
429                                         self.fatal(e)
430
431         def err_handler(self, fun, error):
432                 pass
433
434 def conf(f):
435         "decorator: attach new configuration functions"
436         setattr(ConfigurationContext, f.__name__, f)
437         return f
438
439 def conftest(f):
440         "decorator: attach new configuration tests (registered as strings)"
441         ConfigurationContext.tests[f.__name__] = f
442         return conf(f)