s3: libsmbclient: Add missing talloc stackframe.
[samba.git] / buildtools / wafadmin / Tools / misc.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2006 (ita)
4
5 """
6 Custom objects:
7  - execute a function everytime
8  - copy a file somewhere else
9 """
10
11 import shutil, re, os
12 import TaskGen, Node, Task, Utils, Build, Constants
13 from TaskGen import feature, taskgen, after, before
14 from Logs import debug
15
16 def copy_func(tsk):
17         "Make a file copy. This might be used to make other kinds of file processing (even calling a compiler is possible)"
18         env = tsk.env
19         infile = tsk.inputs[0].abspath(env)
20         outfile = tsk.outputs[0].abspath(env)
21         try:
22                 shutil.copy2(infile, outfile)
23         except (OSError, IOError):
24                 return 1
25         else:
26                 if tsk.chmod: os.chmod(outfile, tsk.chmod)
27                 return 0
28
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!')
32         return tsk.fun(tsk)
33
34 class cmd_taskgen(TaskGen.task_gen):
35         def __init__(self, *k, **kw):
36                 TaskGen.task_gen.__init__(self, *k, **kw)
37
38 @feature('cmd')
39 def apply_cmd(self):
40         "call a command everytime"
41         if not self.fun: raise Utils.WafError('cmdobj needs a function!')
42         tsk = Task.TaskBase()
43         tsk.fun = self.fun
44         tsk.env = self.env
45         self.tasks.append(tsk)
46         tsk.install_path = self.install_path
47
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)
52
53 @feature('copy')
54 @before('apply_core')
55 def apply_copy(self):
56         Utils.def_attrs(self, fun=copy_func)
57         self.default_install_path = 0
58
59         lst = self.to_list(self.source)
60         self.meths.remove('apply_core')
61
62         for filename in lst:
63                 node = self.path.find_resource(filename)
64                 if not node: raise Utils.WafError('cannot find input file %s for processing' % filename)
65
66                 target = self.target
67                 if not target or len(lst)>1: target = node.name
68
69                 # TODO the file path may be incorrect
70                 newnode = self.path.find_or_declare(target)
71
72                 tsk = self.create_task('copy', node, newnode)
73                 tsk.fun = self.fun
74                 tsk.chmod = self.chmod
75                 tsk.install_path = self.install_path
76
77                 if not tsk.env:
78                         tsk.debug()
79                         raise Utils.WafError('task without an environment')
80
81 def subst_func(tsk):
82         "Substitutes variables in a .in file"
83
84         m4_re = re.compile('@(\w+)@', re.M)
85
86         env = tsk.env
87         infile = tsk.inputs[0].abspath(env)
88         outfile = tsk.outputs[0].abspath(env)
89
90         code = Utils.readf(infile)
91
92         # replace all % by %% to prevent errors by % signs in the input file while string formatting
93         code = code.replace('%', '%%')
94
95         s = m4_re.sub(r'%(\1)s', code)
96
97         di = tsk.dict or {}
98         if not di:
99                 names = m4_re.findall(code)
100                 for i in names:
101                         di[i] = env.get_flat(i) or env.get_flat(i.upper())
102
103         file = open(outfile, 'w')
104         file.write(s % di)
105         file.close()
106         if tsk.chmod: os.chmod(outfile, tsk.chmod)
107
108 class subst_taskgen(TaskGen.task_gen):
109         def __init__(self, *k, **kw):
110                 TaskGen.task_gen.__init__(self, *k, **kw)
111
112 @feature('subst')
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')
119
120         self.dict = getattr(self, 'dict', {})
121
122         for filename in lst:
123                 node = self.path.find_resource(filename)
124                 if not node: raise Utils.WafError('cannot find input file %s for processing' % filename)
125
126                 if self.target:
127                         newnode = self.path.find_or_declare(self.target)
128                 else:
129                         newnode = node.change_ext('')
130
131                 try:
132                         self.dict = self.dict.get_merged_dict()
133                 except AttributeError:
134                         pass
135
136                 if self.dict and not self.env['DICT_HASH']:
137                         self.env = self.env.copy()
138                         keys = list(self.dict.keys())
139                         keys.sort()
140                         lst = [self.dict[x] for x in keys]
141                         self.env['DICT_HASH'] = str(Utils.h_list(lst))
142
143                 tsk = self.create_task('copy', node, newnode)
144                 tsk.fun = self.fun
145                 tsk.dict = self.dict
146                 tsk.dep_vars = ['DICT_HASH']
147                 tsk.install_path = self.install_path
148                 tsk.chmod = self.chmod
149
150                 if not tsk.env:
151                         tsk.debug()
152                         raise Utils.WafError('task without an environment')
153
154 ####################
155 ## command-output ####
156 ####################
157
158 class cmd_arg(object):
159         """command-output arguments for representing files or folders"""
160         def __init__(self, name, template='%s'):
161                 self.name = name
162                 self.template = template
163                 self.node = None
164
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))
171
172         def get_path(self, env, absolute):
173                 if absolute:
174                         return self.template % self.node.abspath(env)
175                 else:
176                         return self.template % self.node.srcpath(env)
177
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))
184
185         def get_path(self, env, absolute):
186                 if absolute:
187                         return self.template % self.node.abspath(env)
188                 else:
189                         return self.template % self.node.bldpath(env)
190
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))
197
198 class input_dir(cmd_dir_arg):
199         def get_path(self, dummy_env, dummy_absolute):
200                 return self.template % self.node.abspath()
201
202 class output_dir(cmd_dir_arg):
203         def get_path(self, env, dummy_absolute):
204                 return self.template % self.node.abspath(env)
205
206
207 class command_output(Task.Task):
208         color = "BLUE"
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
214                 self.stdin = stdin
215                 self.stdout = stdout
216                 self.cwd = cwd
217                 self.os_env = os_env
218                 self.stderr = stderr
219
220                 if command_node is not None: self.dep_nodes = [command_node]
221                 self.dep_vars = [] # additional environment variables to look
222
223         def run(self):
224                 task = self
225                 #assert len(task.inputs) > 0
226
227                 def input_path(node, template):
228                         if task.cwd is None:
229                                 return template % node.bldpath(task.env)
230                         else:
231                                 return template % node.abspath()
232                 def output_path(node, template):
233                         fun = node.abspath
234                         if task.cwd is None: fun = node.bldpath
235                         return template % fun(task.env)
236
237                 if isinstance(task.command, Node.Node):
238                         argv = [input_path(task.command, '%s')]
239                 else:
240                         argv = [task.command]
241
242                 for arg in task.command_args:
243                         if isinstance(arg, str):
244                                 argv.append(arg)
245                         else:
246                                 assert isinstance(arg, cmd_arg)
247                                 argv.append(arg.get_path(task.env, (task.cwd is not None)))
248
249                 if task.stdin:
250                         stdin = open(input_path(task.stdin, '%s'))
251                 else:
252                         stdin = None
253
254                 if task.stdout:
255                         stdout = open(output_path(task.stdout, '%s'), "w")
256                 else:
257                         stdout = None
258
259                 if task.stderr:
260                         stderr = open(output_path(task.stderr, '%s'), "w")
261                 else:
262                         stderr = None
263
264                 if task.cwd is None:
265                         cwd = ('None (actually %r)' % os.getcwd())
266                 else:
267                         cwd = repr(task.cwd)
268                 debug("command-output: cwd=%s, stdin=%r, stdout=%r, argv=%r" %
269                              (cwd, stdin, stdout, argv))
270
271                 if task.os_env is None:
272                         os_env = os.environ
273                 else:
274                         os_env = task.os_env
275                 command = Utils.pproc.Popen(argv, stdin=stdin, stdout=stdout, stderr=stderr, cwd=task.cwd, env=os_env)
276                 return command.wait()
277
278 class cmd_output_taskgen(TaskGen.task_gen):
279         def __init__(self, *k, **kw):
280                 TaskGen.task_gen.__init__(self, *k, **kw)
281
282 @feature('command-output')
283 def init_cmd_output(self):
284         Utils.def_attrs(self,
285                 stdin = None,
286                 stdout = None,
287                 stderr = None,
288                 # the command to execute
289                 command = None,
290
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,
295
296                 # extra parameters (argv) to pass to the command (excluding
297                 # the command itself)
298                 argv = [],
299
300                 # dependencies to other objects -> this is probably not what you want (ita)
301                 # values must be 'task_gen' instances (not names!)
302                 dependencies = [],
303
304                 # dependencies on env variable contents
305                 dep_vars = [],
306
307                 # input files that are implicit, i.e. they are not
308                 # stdin, nor are they mentioned explicitly in argv
309                 hidden_inputs = [],
310
311                 # output files that are implicit, i.e. they are not
312                 # stdout, nor are they mentioned explicitly in argv
313                 hidden_outputs = [],
314
315                 # change the subprocess to this cwd (must use obj.input_dir() or output_dir() here)
316                 cwd = None,
317
318                 # OS environment variables to pass to the subprocess
319                 # if None, use the default environment variables unchanged
320                 os_env = None)
321
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:
328                 cmd = self.command
329                 cmd_node = None
330         else:
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,)
335                 cmd = cmd_node
336
337         if self.cwd is None:
338                 cwd = None
339         else:
340                 assert isinstance(cwd, CmdDirArg)
341                 self.cwd.find_node(self.path)
342
343         args = []
344         inputs = []
345         outputs = []
346
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)
354
355         if self.stdout is None:
356                 stdout = None
357         else:
358                 assert isinstance(self.stdout, str)
359                 stdout = self.path.find_or_declare(self.stdout)
360                 if stdout is None:
361                         raise Utils.WafError("File %s not found" % (self.stdout,))
362                 outputs.append(stdout)
363
364         if self.stderr is None:
365                 stderr = None
366         else:
367                 assert isinstance(self.stderr, str)
368                 stderr = self.path.find_or_declare(self.stderr)
369                 if stderr is None:
370                         raise Utils.WafError("File %s not found" % (self.stderr,))
371                 outputs.append(stderr)
372
373         if self.stdin is None:
374                 stdin = None
375         else:
376                 assert isinstance(self.stdin, str)
377                 stdin = self.path.find_resource(self.stdin)
378                 if stdin is None:
379                         raise Utils.WafError("File %s not found" % (self.stdin,))
380                 inputs.append(stdin)
381
382         for hidden_input in self.to_list(self.hidden_inputs):
383                 node = self.path.find_resource(hidden_input)
384                 if node is None:
385                         raise Utils.WafError("File %s not found in dir %s" % (hidden_input, self.path))
386                 inputs.append(node)
387
388         for hidden_output in self.to_list(self.hidden_outputs):
389                 node = self.path.find_or_declare(hidden_output)
390                 if node is None:
391                         raise Utils.WafError("File %s not found in dir %s" % (hidden_output, self.path))
392                 outputs.append(node)
393
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')
398
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)
402
403         task.inputs = inputs
404         task.outputs = outputs
405         task.dep_vars = self.to_list(self.dep_vars)
406
407         for dep in self.dependencies:
408                 assert dep is not self
409                 dep.post()
410                 for dep_task in dep.tasks:
411                         task.set_run_after(dep_task)
412
413         if not task.inputs:
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__)
417
418         # TODO the case with no outputs?
419
420 def post_run(self):
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
424
425 def runnable_status(self):
426         return Constants.RUN_ME
427
428 Task.task_type_from_func('copy', vars=[], func=action_process_file_func)
429 TaskGen.task_gen.classes['command-output'] = cmd_output_taskgen
430