Sophie

Sophie

distrib > Fedora > 14 > i386 > by-pkgid > 42620103d8ee8a2d972d3103bad0ab73 > files > 306

waf-1.5.19-1.fc14.noarch.rpm

#! /usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2008 (ita)

"""
Example of a compiler that generates cpp files in arbitrary folders
It is not possible to know the files in advance, but the compiler
does not have to run each time, and the cpp tasks must be created to
compile and link the cpp files produced.

Also, the c++ tasks must be created each time

This demonstrates:
* the use of the exclusive_build_node method
* how to hash files manually and update the node information
* define a task without output nodes
* how to manage ill-behaving compilers

The node method find_dir (and find_resource or find_or_declare) assume that
the directory structure exists in both the build and source folders,
it is therefore necesary to use the method exclusive_build_node instead
"""

## A DEMO ##

VERSION='0.0.1'
APPNAME='unknown_outputs'
top = '.'
out = 'build'

def configure(conf):
	# used only when configured from the same folder
	conf.check_tool('gcc')
	conf.env.SHPIP_COMPILER = os.getcwd() + os.sep + "bad_compiler.py"

def build(bld):
	staticlib = bld(features='cc cstaticlib')
	staticlib.source = 'x.c foo.shpip'
	staticlib.target='teststaticlib'
	staticlib.includes = '.'


## INTERNAL CODE BELOW ##

import os
import TaskGen, Task, Utils, Build
from TaskGen import taskgen, feature, before, after, extension
from Logs import debug
from Constants import *

@taskgen
@after('apply_link')
@extension('.shpip')
def process_shpip(self, node):
	tsk = shpip_task(self.env, generator=self)
	tsk.task_gen = self
	tsk.set_inputs(node)

class shpip_task(Task.Task):
	"""
	A special task, which finds its outputs once it has run
	It outputs cpp files that must be compiled too
	"""

	color = 'PINK'
	quiet = 1

	# important, no link before all shpip are done
	before = ['cc_link', 'cxx_link', 'ar_link_static', 'cc', 'cxx']

	def __init__(self, *k, **kw):
		Task.Task.__init__(self, *k, **kw)

	def run(self):
		"runs a program that creates cpp files, capture the output to compile them"
		node = self.inputs[0]
		bld = self.generator.bld

		dir = bld.srcnode.bldpath(self.env)
		cmd = 'cd %s && %s %s' % (dir, self.env.SHPIP_COMPILER, node.abspath(self.env))
		try:
			# read the contents of the file and create cpp files from it
			files = os.popen(cmd).read().strip()
		except:
			# comment the following line to disable debugging
			#raise
			return 1 # error

		# the variable lst should contain a list of paths to the files produced
		lst = Utils.to_list(files)

		# Waf does not know "magically" what files are produced
		# In the most general case it may be necessary to run os.listdir() to see them
		# In this demo the command outputs is giving us this list

		# the files exist in the build dir only so we do not use find_or_declare
		build_nodes = [node.parent.exclusive_build_node(x) for x in lst]
		self.outputs = build_nodes

		# create the cpp tasks
		self.more_tasks = self.add_cpp_tasks(build_nodes)

		# cache the file names and the task signature
		sig = self.signature()
		bld.raw_deps[self.unique_id()] = [sig] + lst

		return 0 # no error

	def runnable_status(self):
		# look at the cache, if the shpip task was already run
		# and if the source has not changed, create the corresponding cpp tasks

		for t in self.run_after:
			if not t.hasrun:
				return ASK_LATER

		tree = self.generator.bld

		try:
			sig = self.signature()
			key = self.unique_id()
			deps = tree.raw_deps[key]
			prev_sig = tree.task_sigs[key][0]
		except KeyError:
			pass
		else:
			# if the file has not changed, create the cpp tasks
			if prev_sig == sig:
				nodes = [self.task_gen.path.exclusive_build_node(y) for y in deps[1:]]
				self.set_outputs(nodes)
				tsklst = self.add_cpp_tasks(nodes)
				generator = tree.generator
				for tsk in tsklst:
					generator.outstanding.append(tsk)

		if not self.outputs:
			return RUN_ME

		# this is a part of Task.Task:runnable_status: first node does not exist -> run
		# this is necessary after a clean
		env = self.env
		node = self.outputs[0]
		variant = node.variant(env)

		try:
			time = tree.node_sigs[variant][node.id]
		except KeyError:
			debug("task: task #%d should run as the first node does not exist" % self.idx)
			try: new_sig = self.signature()
			except KeyError:
				print "TODO - computing the signature failed"
				return RUN_ME

			ret = self.can_retrieve_cache(new_sig)
			return ret and SKIP_ME or RUN_ME

		return SKIP_ME

	def add_cpp_tasks(self, lst):
		"creates cpp tasks after the build has started"
		tgen = self.task_gen
		tsklst = []

		for node in lst:
			if node.name.endswith('.h'):
				continue

			TaskGen.task_gen.mapped['c_hook'](tgen, node)
			task = tgen.compiled_tasks[-1]
			task.set_run_after(self)

			# important, no link before compilations are all over
			try:
				self.generator.link_task.set_run_after(task)
			except AttributeError:
				pass

			tgen.link_task.inputs.append(task.outputs[0])
			tsklst.append(task)

			# if headers are produced something like this can be done to add the include paths
			dir = task.inputs[0].parent
			cpppath_st = self.env.CPPPATH_ST
			self.env.append_unique('_CXXINCFLAGS', cpppath_st % dir.abspath(self.env)) # include paths for c++
			self.env.append_unique('_CCINCFLAGS', cpppath_st % dir.abspath(self.env)) # include paths for c
			self.env.append_value('INC_PATHS', dir) # for the waf preprocessor

		return tsklst