diff options
Diffstat (limited to 'waflib/extras/swig.py')
| -rw-r--r-- | waflib/extras/swig.py | 237 | 
1 files changed, 237 insertions, 0 deletions
diff --git a/waflib/extras/swig.py b/waflib/extras/swig.py new file mode 100644 index 0000000..fd3d6d2 --- /dev/null +++ b/waflib/extras/swig.py @@ -0,0 +1,237 @@ +#! /usr/bin/env python +# encoding: UTF-8 +# Petar Forai +# Thomas Nagy 2008-2010 (ita) + +import re +from waflib import Task, Logs +from waflib.TaskGen import extension, feature, after_method +from waflib.Configure import conf +from waflib.Tools import c_preproc + +""" +tasks have to be added dynamically: +- swig interface files may be created at runtime +- the module name may be unknown in advance +""" + +SWIG_EXTS = ['.swig', '.i'] + +re_module = re.compile('%module(?:\s*\(.*\))?\s+(.+)', re.M) + +re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M) +re_2 = re.compile('[#%]include [<"](.*)[">]', re.M) + +class swig(Task.Task): +	color   = 'BLUE' +	run_str = '${SWIG} ${SWIGFLAGS} ${SWIGPATH_ST:INCPATHS} ${SWIGDEF_ST:DEFINES} ${SRC}' +	ext_out = ['.h'] # might produce .h files although it is not mandatory +	vars = ['SWIG_VERSION', 'SWIGDEPS'] + +	def runnable_status(self): +		for t in self.run_after: +			if not t.hasrun: +				return Task.ASK_LATER + +		if not getattr(self, 'init_outputs', None): +			self.init_outputs = True +			if not getattr(self, 'module', None): +				# search the module name +				txt = self.inputs[0].read() +				m = re_module.search(txt) +				if not m: +					raise ValueError("could not find the swig module name") +				self.module = m.group(1) + +			swig_c(self) + +			# add the language-specific output files as nodes +			# call funs in the dict swig_langs +			for x in self.env['SWIGFLAGS']: +				# obtain the language +				x = x[1:] +				try: +					fun = swig_langs[x] +				except KeyError: +					pass +				else: +					fun(self) + +		return super(swig, self).runnable_status() + +	def scan(self): +		"scan for swig dependencies, climb the .i files" +		lst_src = [] + +		seen = [] +		missing = [] +		to_see = [self.inputs[0]] + +		while to_see: +			node = to_see.pop(0) +			if node in seen: +				continue +			seen.append(node) +			lst_src.append(node) + +			# read the file +			code = node.read() +			code = c_preproc.re_nl.sub('', code) +			code = c_preproc.re_cpp.sub(c_preproc.repl, code) + +			# find .i files and project headers +			names = re_2.findall(code) +			for n in names: +				for d in self.generator.includes_nodes + [node.parent]: +					u = d.find_resource(n) +					if u: +						to_see.append(u) +						break +				else: +					missing.append(n) +		return (lst_src, missing) + +# provide additional language processing +swig_langs = {} +def swigf(fun): +	swig_langs[fun.__name__.replace('swig_', '')] = fun +	return fun +swig.swigf = swigf + +def swig_c(self): +	ext = '.swigwrap_%d.c' % self.generator.idx +	flags = self.env['SWIGFLAGS'] +	if '-c++' in flags: +		ext += 'xx' +	out_node = self.inputs[0].parent.find_or_declare(self.module + ext) + +	if '-c++' in flags: +		c_tsk = self.generator.cxx_hook(out_node) +	else: +		c_tsk = self.generator.c_hook(out_node) + +	c_tsk.set_run_after(self) + +	# transfer weights from swig task to c task +	if getattr(self, 'weight', None): +		c_tsk.weight = self.weight +	if getattr(self, 'tree_weight', None): +		c_tsk.tree_weight = self.tree_weight + +	try: +		self.more_tasks.append(c_tsk) +	except AttributeError: +		self.more_tasks = [c_tsk] + +	try: +		ltask = self.generator.link_task +	except AttributeError: +		pass +	else: +		ltask.set_run_after(c_tsk) +		# setting input nodes does not declare the build order +		# because the build already started, but it sets +		# the dependency to enable rebuilds +		ltask.inputs.append(c_tsk.outputs[0]) + +	self.outputs.append(out_node) + +	if not '-o' in self.env['SWIGFLAGS']: +		self.env.append_value('SWIGFLAGS', ['-o', self.outputs[0].abspath()]) + +@swigf +def swig_python(tsk): +	node = tsk.inputs[0].parent +	if tsk.outdir: +		node = tsk.outdir +	tsk.set_outputs(node.find_or_declare(tsk.module+'.py')) + +@swigf +def swig_ocaml(tsk): +	node = tsk.inputs[0].parent +	if tsk.outdir: +		node = tsk.outdir +	tsk.set_outputs(node.find_or_declare(tsk.module+'.ml')) +	tsk.set_outputs(node.find_or_declare(tsk.module+'.mli')) + +@extension(*SWIG_EXTS) +def i_file(self, node): +	# the task instance +	tsk = self.create_task('swig') +	tsk.set_inputs(node) +	tsk.module = getattr(self, 'swig_module', None) + +	flags = self.to_list(getattr(self, 'swig_flags', [])) +	tsk.env.append_value('SWIGFLAGS', flags) + +	tsk.outdir = None +	if '-outdir' in flags: +		outdir = flags[flags.index('-outdir')+1] +		outdir = tsk.generator.bld.bldnode.make_node(outdir) +		outdir.mkdir() +		tsk.outdir = outdir + +@feature('c', 'cxx', 'd', 'fc', 'asm') +@after_method('apply_link', 'process_source') +def enforce_swig_before_link(self): +	try: +		link_task = self.link_task +	except AttributeError: +		pass +	else: +		for x in self.tasks: +			if x.__class__.__name__ == 'swig': +				link_task.run_after.add(x) + +@conf +def check_swig_version(conf, minver=None): +	""" +	Check if the swig tool is found matching a given minimum version. +	minver should be a tuple, eg. to check for swig >= 1.3.28 pass (1,3,28) as minver. + +	If successful, SWIG_VERSION is defined as 'MAJOR.MINOR' +	(eg. '1.3') of the actual swig version found. + +	:param minver: minimum version +	:type minver: tuple of int +	:return: swig version +	:rtype: tuple of int +	""" +	assert minver is None or isinstance(minver, tuple) +	swigbin = conf.env['SWIG'] +	if not swigbin: +		conf.fatal('could not find the swig executable') + +	# Get swig version string +	cmd = swigbin + ['-version'] +	Logs.debug('swig: Running swig command %r', cmd) +	reg_swig = re.compile(r'SWIG Version\s(.*)', re.M) +	swig_out = conf.cmd_and_log(cmd) +	swigver_tuple = tuple([int(s) for s in reg_swig.findall(swig_out)[0].split('.')]) + +	# Compare swig version with the minimum required +	result = (minver is None) or (swigver_tuple >= minver) + +	if result: +		# Define useful environment variables +		swigver = '.'.join([str(x) for x in swigver_tuple[:2]]) +		conf.env['SWIG_VERSION'] = swigver + +	# Feedback +	swigver_full = '.'.join(map(str, swigver_tuple[:3])) +	if minver is None: +		conf.msg('Checking for swig version', swigver_full) +	else: +		minver_str = '.'.join(map(str, minver)) +		conf.msg('Checking for swig version >= %s' % (minver_str,), swigver_full, color=result and 'GREEN' or 'YELLOW') + +	if not result: +		conf.fatal('The swig version is too old, expecting %r' % (minver,)) + +	return swigver_tuple + +def configure(conf): +	conf.find_program('swig', var='SWIG') +	conf.env.SWIGPATH_ST = '-I%s' +	conf.env.SWIGDEF_ST = '-D%s' +  |