CVE-2015-7560: s3: smbd: Refuse to set a POSIX ACL on a symlink.
[Samba.git] / buildtools / wafadmin / Configure.py
blob35b4e51ec35a09721fbcf32e086e475022272e9f
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2008 (ita)
5 """
6 Configuration system
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
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)
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, ..).
19 Note: the c/c++ related code is in the module config_c
20 """
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 *
29 try:
30 from urllib import request
31 except:
32 from urllib import urlopen
33 else:
34 urlopen = request.urlopen
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
40 '''
42 class ConfigurationError(Utils.WscriptError):
43 pass
45 autoconfig = False
46 "reconfigure the project automatically"
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 ''
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 """
69 if not environ:
70 environ = os.environ
72 try: path_list = path_list.split()
73 except AttributeError: pass
75 if var:
76 if env[var]: return env[var]
77 if var in environ: env[var] = environ[var]
79 if not path_list: path_list = environ.get('PATH', '').split(os.pathsep)
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 ''
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 = ''
97 self.environ = dict(os.environ)
99 self.line_just = 40
101 self.blddir = blddir
102 self.srcdir = srcdir
103 self.all_envs = {}
105 # curdir: necessary for recursion
106 self.cwd = self.curdir = os.getcwd()
108 self.tools = [] # tools loaded in the configuration, and that will be loaded when building
110 self.setenv(DEFAULT)
112 self.lastprog = ''
114 self.hash = 0
115 self.files = []
117 self.tool_cache = []
119 if self.blddir:
120 self.post_init()
122 def post_init(self):
124 self.cachedir = os.path.join(self.blddir, CACHE_DIR)
126 path = os.path.join(self.blddir, WAF_CONFIG_LOG)
127 try: os.unlink(path)
128 except (OSError, IOError): pass
130 try:
131 self.log = open(path, 'w')
132 except (OSError, IOError):
133 self.fatal('could not open %r for writing' % path)
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)
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())
149 def __del__(self):
150 """cleanup function: close config.log"""
152 # may be ran by the gc, not always after initialization
153 if hasattr(self, 'log') and self.log:
154 self.log.close()
156 def fatal(self, msg):
157 raise ConfigurationError(msg)
159 def check_tool(self, input, tooldir=None, funs=None):
160 "load a waf tool"
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
171 mag = (tool, id(self.env), funs)
172 if mag in self.tool_cache:
173 continue
174 self.tool_cache.append(mag)
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')
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
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
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)
233 self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs})
235 def sub_config(self, k):
236 "executes the configure function of a wscript module"
237 self.recurse(k, name='configure')
239 def pre_recurse(self, name_or_mod, path, nexdir):
240 return {'conf': self, 'ctx': self}
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)
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)
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()
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))
265 def set_env_name(self, name, env):
266 "add a new environment called name"
267 self.all_envs[name] = env
268 return env
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
282 def setenv(self, name):
283 "enable the environment called name"
284 self.env = self.retrieve(name)
285 self.envname = name
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
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='')
298 def check_message_2(self, sr, color='GREEN'):
299 self.log.write(sr)
300 self.log.write('\n')
301 Utils.pprint(color, sr)
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')
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)
317 def msg(self, msg, result, color=None):
318 """Prints a configuration message 'Checking for xxx: ok'"""
319 self.start_msg('Checking for ' + msg)
321 if not isinstance(color, str):
322 color = result and 'GREEN' or 'YELLOW'
324 self.end_msg(result, color)
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
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='')
339 def end_msg(self, result, color):
340 self.in_msg -= 1
341 if self.in_msg:
342 return
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)
354 self.log.write(msg)
355 self.log.write('\n')
356 Utils.pprint(color, msg)
358 def find_program(self, filename, path_list=[], var=None, mandatory=False):
359 "wrapper that adds a configuration message"
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]
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
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)
383 if var:
384 self.env[var] = ret
385 return ret
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
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_'):
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)
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:
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)
431 def err_handler(self, fun, error):
432 pass
434 def conf(f):
435 "decorator: attach new configuration functions"
436 setattr(ConfigurationContext, f.__name__, f)
437 return f
439 def conftest(f):
440 "decorator: attach new configuration tests (registered as strings)"
441 ConfigurationContext.tests[f.__name__] = f
442 return conf(f)