3 # Thomas Nagy, 2006 (ita)
7 - execute a function everytime
8 - copy a file somewhere else
12 import TaskGen, Node, Task, Utils, Build, Constants
13 from TaskGen import feature, taskgen, after, before
14 from Logs import debug
17 "Make a file copy. This might be used to make other kinds of file processing (even calling a compiler is possible)"
19 infile = tsk.inputs[0].abspath(env)
20 outfile = tsk.outputs[0].abspath(env)
22 shutil.copy2(infile, outfile)
23 except (OSError, IOError):
26 if tsk.chmod: os.chmod(outfile, tsk.chmod)
29 def action_process_file_func(tsk):
30 "Ask the function attached to the task to process it"
31 if not tsk.fun: raise Utils.WafError('task must have a function attached to it for copy_func to work!')
34 class cmd_taskgen(TaskGen.task_gen):
35 def __init__(self, *k, **kw):
36 TaskGen.task_gen.__init__(self, *k, **kw)
40 "call a command everytime"
41 if not self.fun: raise Utils.WafError('cmdobj needs a function!')
45 self.tasks.append(tsk)
46 tsk.install_path = self.install_path
48 class copy_taskgen(TaskGen.task_gen):
49 "By default, make a file copy, if fun is provided, fun will make the copy (or call a compiler, etc)"
50 def __init__(self, *k, **kw):
51 TaskGen.task_gen.__init__(self, *k, **kw)
56 Utils.def_attrs(self, fun=copy_func)
57 self.default_install_path = 0
59 lst = self.to_list(self.source)
60 self.meths.remove('apply_core')
63 node = self.path.find_resource(filename)
64 if not node: raise Utils.WafError('cannot find input file %s for processing' % filename)
67 if not target or len(lst)>1: target = node.name
69 # TODO the file path may be incorrect
70 newnode = self.path.find_or_declare(target)
72 tsk = self.create_task('copy', node, newnode)
74 tsk.chmod = self.chmod
75 tsk.install_path = self.install_path
79 raise Utils.WafError('task without an environment')
82 "Substitutes variables in a .in file"
84 m4_re = re.compile('@(\w+)@', re.M)
87 infile = tsk.inputs[0].abspath(env)
88 outfile = tsk.outputs[0].abspath(env)
90 code = Utils.readf(infile)
92 # replace all % by %% to prevent errors by % signs in the input file while string formatting
93 code = code.replace('%', '%%')
95 s = m4_re.sub(r'%(\1)s', code)
99 names = m4_re.findall(code)
101 di[i] = env.get_flat(i) or env.get_flat(i.upper())
103 file = open(outfile, 'w')
106 if tsk.chmod: os.chmod(outfile, tsk.chmod)
108 class subst_taskgen(TaskGen.task_gen):
109 def __init__(self, *k, **kw):
110 TaskGen.task_gen.__init__(self, *k, **kw)
113 @before('apply_core')
114 def apply_subst(self):
115 Utils.def_attrs(self, fun=subst_func)
116 self.default_install_path = 0
117 lst = self.to_list(self.source)
118 self.meths.remove('apply_core')
120 self.dict = getattr(self, 'dict', {})
123 node = self.path.find_resource(filename)
124 if not node: raise Utils.WafError('cannot find input file %s for processing' % filename)
127 newnode = self.path.find_or_declare(self.target)
129 newnode = node.change_ext('')
132 self.dict = self.dict.get_merged_dict()
133 except AttributeError:
136 if self.dict and not self.env['DICT_HASH']:
137 self.env = self.env.copy()
138 keys = list(self.dict.keys())
140 lst = [self.dict[x] for x in keys]
141 self.env['DICT_HASH'] = str(Utils.h_list(lst))
143 tsk = self.create_task('copy', node, newnode)
146 tsk.dep_vars = ['DICT_HASH']
147 tsk.install_path = self.install_path
148 tsk.chmod = self.chmod
152 raise Utils.WafError('task without an environment')
155 ## command-output ####
158 class cmd_arg(object):
159 """command-output arguments for representing files or folders"""
160 def __init__(self, name, template='%s'):
162 self.template = template
165 class input_file(cmd_arg):
166 def find_node(self, base_path):
167 assert isinstance(base_path, Node.Node)
168 self.node = base_path.find_resource(self.name)
169 if self.node is None:
170 raise Utils.WafError("Input file %s not found in " % (self.name, base_path))
172 def get_path(self, env, absolute):
174 return self.template % self.node.abspath(env)
176 return self.template % self.node.srcpath(env)
178 class output_file(cmd_arg):
179 def find_node(self, base_path):
180 assert isinstance(base_path, Node.Node)
181 self.node = base_path.find_or_declare(self.name)
182 if self.node is None:
183 raise Utils.WafError("Output file %s not found in " % (self.name, base_path))
185 def get_path(self, env, absolute):
187 return self.template % self.node.abspath(env)
189 return self.template % self.node.bldpath(env)
191 class cmd_dir_arg(cmd_arg):
192 def find_node(self, base_path):
193 assert isinstance(base_path, Node.Node)
194 self.node = base_path.find_dir(self.name)
195 if self.node is None:
196 raise Utils.WafError("Directory %s not found in " % (self.name, base_path))
198 class input_dir(cmd_dir_arg):
199 def get_path(self, dummy_env, dummy_absolute):
200 return self.template % self.node.abspath()
202 class output_dir(cmd_dir_arg):
203 def get_path(self, env, dummy_absolute):
204 return self.template % self.node.abspath(env)
207 class command_output(Task.Task):
209 def __init__(self, env, command, command_node, command_args, stdin, stdout, cwd, os_env, stderr):
210 Task.Task.__init__(self, env, normal=1)
211 assert isinstance(command, (str, Node.Node))
212 self.command = command
213 self.command_args = command_args
220 if command_node is not None: self.dep_nodes = [command_node]
221 self.dep_vars = [] # additional environment variables to look
225 #assert len(task.inputs) > 0
227 def input_path(node, template):
229 return template % node.bldpath(task.env)
231 return template % node.abspath()
232 def output_path(node, template):
234 if task.cwd is None: fun = node.bldpath
235 return template % fun(task.env)
237 if isinstance(task.command, Node.Node):
238 argv = [input_path(task.command, '%s')]
240 argv = [task.command]
242 for arg in task.command_args:
243 if isinstance(arg, str):
246 assert isinstance(arg, cmd_arg)
247 argv.append(arg.get_path(task.env, (task.cwd is not None)))
250 stdin = open(input_path(task.stdin, '%s'))
255 stdout = open(output_path(task.stdout, '%s'), "w")
260 stderr = open(output_path(task.stderr, '%s'), "w")
265 cwd = ('None (actually %r)' % os.getcwd())
268 debug("command-output: cwd=%s, stdin=%r, stdout=%r, argv=%r" %
269 (cwd, stdin, stdout, argv))
271 if task.os_env is None:
275 command = Utils.pproc.Popen(argv, stdin=stdin, stdout=stdout, stderr=stderr, cwd=task.cwd, env=os_env)
276 return command.wait()
278 class cmd_output_taskgen(TaskGen.task_gen):
279 def __init__(self, *k, **kw):
280 TaskGen.task_gen.__init__(self, *k, **kw)
282 @feature('command-output')
283 def init_cmd_output(self):
284 Utils.def_attrs(self,
288 # the command to execute
291 # whether it is an external command; otherwise it is assumed
292 # to be an executable binary or script that lives in the
293 # source or build tree.
294 command_is_external = False,
296 # extra parameters (argv) to pass to the command (excluding
297 # the command itself)
300 # dependencies to other objects -> this is probably not what you want (ita)
301 # values must be 'task_gen' instances (not names!)
304 # dependencies on env variable contents
307 # input files that are implicit, i.e. they are not
308 # stdin, nor are they mentioned explicitly in argv
311 # output files that are implicit, i.e. they are not
312 # stdout, nor are they mentioned explicitly in argv
315 # change the subprocess to this cwd (must use obj.input_dir() or output_dir() here)
318 # OS environment variables to pass to the subprocess
319 # if None, use the default environment variables unchanged
322 @feature('command-output')
323 @after('init_cmd_output')
324 def apply_cmd_output(self):
325 if self.command is None:
326 raise Utils.WafError("command-output missing command")
327 if self.command_is_external:
331 cmd_node = self.path.find_resource(self.command)
332 assert cmd_node is not None, ('''Could not find command '%s' in source tree.
333 Hint: if this is an external command,
334 use command_is_external=True''') % (self.command,)
340 assert isinstance(cwd, CmdDirArg)
341 self.cwd.find_node(self.path)
347 for arg in self.argv:
348 if isinstance(arg, cmd_arg):
349 arg.find_node(self.path)
350 if isinstance(arg, input_file):
351 inputs.append(arg.node)
352 if isinstance(arg, output_file):
353 outputs.append(arg.node)
355 if self.stdout is None:
358 assert isinstance(self.stdout, str)
359 stdout = self.path.find_or_declare(self.stdout)
361 raise Utils.WafError("File %s not found" % (self.stdout,))
362 outputs.append(stdout)
364 if self.stderr is None:
367 assert isinstance(self.stderr, str)
368 stderr = self.path.find_or_declare(self.stderr)
370 raise Utils.WafError("File %s not found" % (self.stderr,))
371 outputs.append(stderr)
373 if self.stdin is None:
376 assert isinstance(self.stdin, str)
377 stdin = self.path.find_resource(self.stdin)
379 raise Utils.WafError("File %s not found" % (self.stdin,))
382 for hidden_input in self.to_list(self.hidden_inputs):
383 node = self.path.find_resource(hidden_input)
385 raise Utils.WafError("File %s not found in dir %s" % (hidden_input, self.path))
388 for hidden_output in self.to_list(self.hidden_outputs):
389 node = self.path.find_or_declare(hidden_output)
391 raise Utils.WafError("File %s not found in dir %s" % (hidden_output, self.path))
394 if not (inputs or getattr(self, 'no_inputs', None)):
395 raise Utils.WafError('command-output objects must have at least one input file or give self.no_inputs')
396 if not (outputs or getattr(self, 'no_outputs', None)):
397 raise Utils.WafError('command-output objects must have at least one output file or give self.no_outputs')
399 task = command_output(self.env, cmd, cmd_node, self.argv, stdin, stdout, cwd, self.os_env, stderr)
400 Utils.copy_attrs(self, task, 'before after ext_in ext_out', only_if_set=True)
401 self.tasks.append(task)
404 task.outputs = outputs
405 task.dep_vars = self.to_list(self.dep_vars)
407 for dep in self.dependencies:
408 assert dep is not self
410 for dep_task in dep.tasks:
411 task.set_run_after(dep_task)
414 # the case for svnversion, always run, and update the output nodes
415 task.runnable_status = type(Task.TaskBase.run)(runnable_status, task, task.__class__) # always run
416 task.post_run = type(Task.TaskBase.run)(post_run, task, task.__class__)
418 # TODO the case with no outputs?
421 for x in self.outputs:
422 h = Utils.h_file(x.abspath(self.env))
423 self.generator.bld.node_sigs[self.env.variant()][x.id] = h
425 def runnable_status(self):
426 return Constants.RUN_ME
428 Task.task_type_from_func('copy', vars=[], func=action_process_file_func)
429 TaskGen.task_gen.classes['command-output'] = cmd_output_taskgen