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