#! /usr/bin/env python # encoding: utf-8 # Thomas Nagy, 2010 (ita) VERSION='0.0.1' APPNAME='cpp_gen' top = '.' out = 'build' def options(opt): opt.load('compiler_cxx') def configure(conf): conf.load('compiler_cxx') conf.check(header_name='stdio.h', features='cxx cxxprogram', mandatory=False) def build(bld): bld.program(source='main.cpp a.cpp', target='app') #-------- import os from waflib import Task, TaskGen, Utils from waflib.Tools import cxx @TaskGen.extension('.cpp') def more_tasks_at_once(self, node): task1 = self.create_task('prog1', node, []) task2 = self.create_compiled_task('cxx', node) def cmpnodes(a, b): return cmp(a.abspath(), b.abspath()) class prog1(Task.Task): before = ['cxxprogram', 'cxxshlib', 'cxxstlib'] def uid(self): """ the unique id of this task should only depend on the file inputs """ m = Utils.md5() up = m.update up(self.__class__.__name__.encode()) for x in self.inputs: up(x.abspath().encode()) return m.digest() def runnable_status(self): """ since it is called after the build has started, any task added must be passed through 'more_tasks' """ for x in self.run_after: if not x.hasrun: return Task.ASK_LATER # so this is a bit special, the goal here is to set the output nodes # and to create the c++ tasks before the normal processing is done sig = self.signature() for x in self.generator.bld.raw_deps.get(sig, []): self.outputs.append(self.generator.bld.srcnode.find_node(x)) if not self.outputs: self.read_outputs_from_cache() if self.outputs: self.create_cxx_task() ret = Task.Task.runnable_status(self) return ret def create_cxx_task(self): """ create a task dynamically during the build notice the use of 'more_tasks' """ tsk = cxx.cxx_hook(self.generator, self.outputs[0]) tsk.set_run_after(self) # the build has started, so the order must be set manually self.more_tasks = [tsk] # add tasks dynamically during the build self.generator.link_task.inputs.append(tsk.outputs[0]) # add another input for the link task try: self.generator.link_task.inputs.sort(cmp=cmpnodes) # eliminate the random order (more tasks like this) except: self.generator.link_task.inputs.sort(key=lambda x: x.abspath()) # python3 self.generator.link_task.set_run_after(tsk) # do not forget to set the build order there too def run(self): """ actual execution this code should not be executed if the files are retrieved from the cache """ if self.inputs[0].name == 'a.cpp': # simulate the creation of an interface file out = self.inputs[0].parent.get_bld().make_node('a.ser.cpp') out.write('\n\n') # read the file system # remember that nodes cannot be created concurrently # so you might to crate a lock if several tasks want the same nodes inp = self.inputs[0] node = inp.parent.get_bld().find_node(inp.name.replace('.cpp', '.ser.cpp')) if node: self.outputs = [node] h_node = inp.parent.find_node(inp.name.replace('.cpp', '.ser.h')) if h_node: self.outputs.append(h_node) # if there are outputs, create a new c++ task if self.outputs: self.create_cxx_task() # access the scanner data self.generator.bld.raw_deps[self.signature()] = [x.path_from(self.generator.bld.srcnode) for x in self.outputs] def read_outputs_from_cache(self): """ set the outputs from the results found in the cache we assume that the files are created in the same folder as the inputs if it is not like this, the nodes should be restored by another system, for example by storing them in a separate file during run() """ env = self.env sig = self.signature() ssig = Utils.to_hex(sig) # first try to access the cache folder for the task dname = os.path.join(self.generator.bld.cache_global, ssig) try: t1 = os.stat(dname).st_mtime except OSError: return None try: lst = os.listdir(dname) except: return for x in lst: self.outputs.append(self.inputs[0].parent.find_or_declare(x)) # not a fresh build and the cache is removed -> remember the files in the scanner data self.generator.bld.raw_deps[self.signature()] = [x.path_from(self.generator.bld.srcnode) for x in self.outputs]