1#! /usr/bin/env python
2# encoding: utf-8
3# Thomas Nagy, 2010 (ita)
4
5"""
6This file is provided to enable compatibility with waf 1.5
7It was enabled by default in waf 1.6, but it is not used in waf 1.7
8"""
9
10import sys
11from waflib import ConfigSet, Logs, Options, Scripting, Task, Build, Configure, Node, Runner, TaskGen, Utils, Errors, Context
12
13# the following is to bring some compatibility with waf 1.5 "import waflib.Configure → import Configure"
14sys.modules['Environment'] = ConfigSet
15ConfigSet.Environment = ConfigSet.ConfigSet
16
17sys.modules['Logs'] = Logs
18sys.modules['Options'] = Options
19sys.modules['Scripting'] = Scripting
20sys.modules['Task'] = Task
21sys.modules['Build'] = Build
22sys.modules['Configure'] = Configure
23sys.modules['Node'] = Node
24sys.modules['Runner'] = Runner
25sys.modules['TaskGen'] = TaskGen
26sys.modules['Utils'] = Utils
27sys.modules['Constants'] = Context
28Context.SRCDIR = ''
29Context.BLDDIR = ''
30
31from waflib.Tools import c_preproc
32sys.modules['preproc'] = c_preproc
33
34from waflib.Tools import c_config
35sys.modules['config_c'] = c_config
36
37ConfigSet.ConfigSet.copy = ConfigSet.ConfigSet.derive
38ConfigSet.ConfigSet.set_variant = Utils.nada
39
40Utils.pproc = Utils.subprocess
41
42Build.BuildContext.add_subdirs = Build.BuildContext.recurse
43Build.BuildContext.new_task_gen = Build.BuildContext.__call__
44Build.BuildContext.is_install = 0
45Node.Node.relpath_gen = Node.Node.path_from
46
47Utils.pproc = Utils.subprocess
48Utils.get_term_cols = Logs.get_term_cols
49
50def cmd_output(cmd, **kw):
51
52	silent = False
53	if 'silent' in kw:
54		silent = kw['silent']
55		del(kw['silent'])
56
57	if 'e' in kw:
58		tmp = kw['e']
59		del(kw['e'])
60		kw['env'] = tmp
61
62	kw['shell'] = isinstance(cmd, str)
63	kw['stdout'] = Utils.subprocess.PIPE
64	if silent:
65		kw['stderr'] = Utils.subprocess.PIPE
66
67	try:
68		p = Utils.subprocess.Popen(cmd, **kw)
69		output = p.communicate()[0]
70	except OSError as e:
71		raise ValueError(str(e))
72
73	if p.returncode:
74		if not silent:
75			msg = "command execution failed: %s -> %r" % (cmd, str(output))
76			raise ValueError(msg)
77		output = ''
78	return output
79Utils.cmd_output = cmd_output
80
81def name_to_obj(self, s, env=None):
82	if Logs.verbose:
83		Logs.warn('compat: change "name_to_obj(name, env)" by "get_tgen_by_name(name)"')
84	return self.get_tgen_by_name(s)
85Build.BuildContext.name_to_obj = name_to_obj
86
87def env_of_name(self, name):
88	try:
89		return self.all_envs[name]
90	except KeyError:
91		Logs.error('no such environment: '+name)
92		return None
93Build.BuildContext.env_of_name = env_of_name
94
95
96def set_env_name(self, name, env):
97	self.all_envs[name] = env
98	return env
99Configure.ConfigurationContext.set_env_name = set_env_name
100
101def retrieve(self, name, fromenv=None):
102	try:
103		env = self.all_envs[name]
104	except KeyError:
105		env = ConfigSet.ConfigSet()
106		self.prepare_env(env)
107		self.all_envs[name] = env
108	else:
109		if fromenv:
110			Logs.warn('The environment %s may have been configured already', name)
111	return env
112Configure.ConfigurationContext.retrieve = retrieve
113
114Configure.ConfigurationContext.sub_config = Configure.ConfigurationContext.recurse
115Configure.ConfigurationContext.check_tool = Configure.ConfigurationContext.load
116Configure.conftest = Configure.conf
117Configure.ConfigurationError = Errors.ConfigurationError
118Utils.WafError = Errors.WafError
119
120Options.OptionsContext.sub_options = Options.OptionsContext.recurse
121Options.OptionsContext.tool_options = Context.Context.load
122Options.Handler = Options.OptionsContext
123
124Task.simple_task_type = Task.task_type_from_func = Task.task_factory
125Task.Task.classes = Task.classes
126
127def setitem(self, key, value):
128	if key.startswith('CCFLAGS'):
129		key = key[1:]
130	self.table[key] = value
131ConfigSet.ConfigSet.__setitem__ = setitem
132
133@TaskGen.feature('d')
134@TaskGen.before('apply_incpaths')
135def old_importpaths(self):
136	if getattr(self, 'importpaths', []):
137		self.includes = self.importpaths
138
139from waflib import Context
140eld = Context.load_tool
141def load_tool(*k, **kw):
142	ret = eld(*k, **kw)
143	if 'set_options' in ret.__dict__:
144		if Logs.verbose:
145			Logs.warn('compat: rename "set_options" to options')
146		ret.options = ret.set_options
147	if 'detect' in ret.__dict__:
148		if Logs.verbose:
149			Logs.warn('compat: rename "detect" to "configure"')
150		ret.configure = ret.detect
151	return ret
152Context.load_tool = load_tool
153
154def get_curdir(self):
155	return self.path.abspath()
156Context.Context.curdir = property(get_curdir, Utils.nada)
157
158def get_srcdir(self):
159	return self.srcnode.abspath()
160Configure.ConfigurationContext.srcdir = property(get_srcdir, Utils.nada)
161
162def get_blddir(self):
163	return self.bldnode.abspath()
164Configure.ConfigurationContext.blddir = property(get_blddir, Utils.nada)
165
166Configure.ConfigurationContext.check_message_1 = Configure.ConfigurationContext.start_msg
167Configure.ConfigurationContext.check_message_2 = Configure.ConfigurationContext.end_msg
168
169rev = Context.load_module
170def load_module(path, encoding=None):
171	ret = rev(path, encoding)
172	if 'set_options' in ret.__dict__:
173		if Logs.verbose:
174			Logs.warn('compat: rename "set_options" to "options" (%r)', path)
175		ret.options = ret.set_options
176	if 'srcdir' in ret.__dict__:
177		if Logs.verbose:
178			Logs.warn('compat: rename "srcdir" to "top" (%r)', path)
179		ret.top = ret.srcdir
180	if 'blddir' in ret.__dict__:
181		if Logs.verbose:
182			Logs.warn('compat: rename "blddir" to "out" (%r)', path)
183		ret.out = ret.blddir
184	Utils.g_module = Context.g_module
185	Options.launch_dir = Context.launch_dir
186	return ret
187Context.load_module = load_module
188
189old_post = TaskGen.task_gen.post
190def post(self):
191	self.features = self.to_list(self.features)
192	if 'cc' in self.features:
193		if Logs.verbose:
194			Logs.warn('compat: the feature cc does not exist anymore (use "c")')
195		self.features.remove('cc')
196		self.features.append('c')
197	if 'cstaticlib' in self.features:
198		if Logs.verbose:
199			Logs.warn('compat: the feature cstaticlib does not exist anymore (use "cstlib" or "cxxstlib")')
200		self.features.remove('cstaticlib')
201		self.features.append(('cxx' in self.features) and 'cxxstlib' or 'cstlib')
202	if getattr(self, 'ccflags', None):
203		if Logs.verbose:
204			Logs.warn('compat: "ccflags" was renamed to "cflags"')
205		self.cflags = self.ccflags
206	return old_post(self)
207TaskGen.task_gen.post = post
208
209def waf_version(*k, **kw):
210	Logs.warn('wrong version (waf_version was removed in waf 1.6)')
211Utils.waf_version = waf_version
212
213
214import os
215@TaskGen.feature('c', 'cxx', 'd')
216@TaskGen.before('apply_incpaths', 'propagate_uselib_vars')
217@TaskGen.after('apply_link', 'process_source')
218def apply_uselib_local(self):
219	"""
220	process the uselib_local attribute
221	execute after apply_link because of the execution order set on 'link_task'
222	"""
223	env = self.env
224	from waflib.Tools.ccroot import stlink_task
225
226	# 1. the case of the libs defined in the project (visit ancestors first)
227	# the ancestors external libraries (uselib) will be prepended
228	self.uselib = self.to_list(getattr(self, 'uselib', []))
229	self.includes = self.to_list(getattr(self, 'includes', []))
230	names = self.to_list(getattr(self, 'uselib_local', []))
231	get = self.bld.get_tgen_by_name
232	seen = set()
233	seen_uselib = set()
234	tmp = Utils.deque(names) # consume a copy of the list of names
235	if tmp:
236		if Logs.verbose:
237			Logs.warn('compat: "uselib_local" is deprecated, replace by "use"')
238	while tmp:
239		lib_name = tmp.popleft()
240		# visit dependencies only once
241		if lib_name in seen:
242			continue
243
244		y = get(lib_name)
245		y.post()
246		seen.add(lib_name)
247
248		# object has ancestors to process (shared libraries): add them to the end of the list
249		if getattr(y, 'uselib_local', None):
250			for x in self.to_list(getattr(y, 'uselib_local', [])):
251				obj = get(x)
252				obj.post()
253				if getattr(obj, 'link_task', None):
254					if not isinstance(obj.link_task, stlink_task):
255						tmp.append(x)
256
257		# link task and flags
258		if getattr(y, 'link_task', None):
259
260			link_name = y.target[y.target.rfind(os.sep) + 1:]
261			if isinstance(y.link_task, stlink_task):
262				env.append_value('STLIB', [link_name])
263			else:
264				# some linkers can link against programs
265				env.append_value('LIB', [link_name])
266
267			# the order
268			self.link_task.set_run_after(y.link_task)
269
270			# for the recompilation
271			self.link_task.dep_nodes += y.link_task.outputs
272
273			# add the link path too
274			tmp_path = y.link_task.outputs[0].parent.bldpath()
275			if not tmp_path in env['LIBPATH']:
276				env.prepend_value('LIBPATH', [tmp_path])
277
278		# add ancestors uselib too - but only propagate those that have no staticlib defined
279		for v in self.to_list(getattr(y, 'uselib', [])):
280			if v not in seen_uselib:
281				seen_uselib.add(v)
282				if not env['STLIB_' + v]:
283					if not v in self.uselib:
284						self.uselib.insert(0, v)
285
286		# if the library task generator provides 'export_includes', add to the include path
287		# the export_includes must be a list of paths relative to the other library
288		if getattr(y, 'export_includes', None):
289			self.includes.extend(y.to_incnodes(y.export_includes))
290
291@TaskGen.feature('cprogram', 'cxxprogram', 'cstlib', 'cxxstlib', 'cshlib', 'cxxshlib', 'dprogram', 'dstlib', 'dshlib')
292@TaskGen.after('apply_link')
293def apply_objdeps(self):
294	"add the .o files produced by some other object files in the same manner as uselib_local"
295	names = getattr(self, 'add_objects', [])
296	if not names:
297		return
298	names = self.to_list(names)
299
300	get = self.bld.get_tgen_by_name
301	seen = []
302	while names:
303		x = names[0]
304
305		# visit dependencies only once
306		if x in seen:
307			names = names[1:]
308			continue
309
310		# object does not exist ?
311		y = get(x)
312
313		# object has ancestors to process first ? update the list of names
314		if getattr(y, 'add_objects', None):
315			added = 0
316			lst = y.to_list(y.add_objects)
317			lst.reverse()
318			for u in lst:
319				if u in seen:
320					continue
321				added = 1
322				names = [u]+names
323			if added:
324				continue # list of names modified, loop
325
326		# safe to process the current object
327		y.post()
328		seen.append(x)
329
330		for t in getattr(y, 'compiled_tasks', []):
331			self.link_task.inputs.extend(t.outputs)
332
333@TaskGen.after('apply_link')
334def process_obj_files(self):
335	if not hasattr(self, 'obj_files'):
336		return
337	for x in self.obj_files:
338		node = self.path.find_resource(x)
339		self.link_task.inputs.append(node)
340
341@TaskGen.taskgen_method
342def add_obj_file(self, file):
343	"""Small example on how to link object files as if they were source
344	obj = bld.create_obj('cc')
345	obj.add_obj_file('foo.o')"""
346	if not hasattr(self, 'obj_files'):
347		self.obj_files = []
348	if not 'process_obj_files' in self.meths:
349		self.meths.append('process_obj_files')
350	self.obj_files.append(file)
351
352
353old_define = Configure.ConfigurationContext.__dict__['define']
354
355@Configure.conf
356def define(self, key, val, quote=True, comment=''):
357	old_define(self, key, val, quote, comment)
358	if key.startswith('HAVE_'):
359		self.env[key] = 1
360
361old_undefine = Configure.ConfigurationContext.__dict__['undefine']
362
363@Configure.conf
364def undefine(self, key, comment=''):
365	old_undefine(self, key, comment)
366	if key.startswith('HAVE_'):
367		self.env[key] = 0
368
369# some people might want to use export_incdirs, but it was renamed
370def set_incdirs(self, val):
371	Logs.warn('compat: change "export_incdirs" by "export_includes"')
372	self.export_includes = val
373TaskGen.task_gen.export_incdirs = property(None, set_incdirs)
374
375def install_dir(self, path):
376	if not path:
377		return []
378
379	destpath = Utils.subst_vars(path, self.env)
380
381	if self.is_install > 0:
382		Logs.info('* creating %s', destpath)
383		Utils.check_dir(destpath)
384	elif self.is_install < 0:
385		Logs.info('* removing %s', destpath)
386		try:
387			os.remove(destpath)
388		except OSError:
389			pass
390Build.BuildContext.install_dir = install_dir
391
392# before/after names
393repl = {'apply_core': 'process_source',
394	'apply_lib_vars': 'process_source',
395	'apply_obj_vars': 'propagate_uselib_vars',
396	'exec_rule': 'process_rule'
397}
398def after(*k):
399	k = [repl.get(key, key) for key in k]
400	return TaskGen.after_method(*k)
401
402def before(*k):
403	k = [repl.get(key, key) for key in k]
404	return TaskGen.before_method(*k)
405TaskGen.before = before
406
407