Merge in upgrade to WAF 1.5.9
authorGustavo J. A. M. Carneiro <gjc@inescporto.pt>
Fri Oct 23 13:05:56 2009 +0100 (3 months ago)
changeset 54940b0222a9fd89
parent 5492 85d025cc5da4
parent 5493 bfa1fc626775
child 5495 a9678e91ec7d
Merge in upgrade to WAF 1.5.9
wscript
     1.1 Binary file waf has changed
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/waf-tools/cflags.py	Fri Oct 23 13:05:56 2009 +0100
     2.3 @@ -0,0 +1,192 @@
     2.4 +import Logs
     2.5 +import Options
     2.6 +import Utils
     2.7 +
     2.8 +
     2.9 +class CompilerTraits(object):
    2.10 +	def get_warnings_flags(self, level):
    2.11 +		"""get_warnings_flags(level) -> list of cflags"""
    2.12 +		raise NotImplementedError
    2.13 +
    2.14 +	def get_optimization_flags(self, level):
    2.15 +		"""get_optimization_flags(level) -> list of cflags"""
    2.16 +		raise NotImplementedError
    2.17 +
    2.18 +	def get_debug_flags(self, level):
    2.19 +		"""get_debug_flags(level) -> (list of cflags, list of cppdefines)"""
    2.20 +		raise NotImplementedError
    2.21 +
    2.22 +
    2.23 +class GccTraits(CompilerTraits):
    2.24 +	def __init__(self):
    2.25 +		super(GccTraits, self).__init__()
    2.26 +		# cumulative list of warnings per level
    2.27 +		self.warnings_flags = [['-Wall'], ['-Werror'], ['-Wextra']]
    2.28 +
    2.29 +	def get_warnings_flags(self, level):
    2.30 +		warnings = []
    2.31 +		for l in range(level):
    2.32 +			if l < len(self.warnings_flags):
    2.33 +				warnings.extend(self.warnings_flags[l])
    2.34 +			else:
    2.35 +				break
    2.36 +		return warnings
    2.37 +
    2.38 +	def get_optimization_flags(self, level):
    2.39 +		if level == 0:
    2.40 +			return ['-O0']
    2.41 +		elif level == 1:
    2.42 +			return ['-O']
    2.43 +		elif level == 2:
    2.44 +			return ['-O2']
    2.45 +		elif level == 3:
    2.46 +			return ['-O3']
    2.47 +
    2.48 +	def get_debug_flags(self, level):
    2.49 +		if level == 0:
    2.50 +			return (['-g0'], ['NDEBUG'])
    2.51 +		elif level == 1:
    2.52 +			return (['-g'], [])
    2.53 +		elif level >= 2:
    2.54 +			return (['-ggdb', '-g3'], ['_DEBUG'])
    2.55 +		
    2.56 +
    2.57 +class IccTraits(CompilerTraits):
    2.58 +	def __init__(self):
    2.59 +		super(IccTraits, self).__init__()
    2.60 +		# cumulative list of warnings per level
    2.61 +		# icc is _very_ verbose with -Wall, -Werror is barely achievable
    2.62 +		self.warnings_flags = [[], [], ['-Wall']]
    2.63 +		
    2.64 +	def get_warnings_flags(self, level):
    2.65 +		warnings = []
    2.66 +		for l in range(level):
    2.67 +			if l < len(self.warnings_flags):
    2.68 +				warnings.extend(self.warnings_flags[l])
    2.69 +			else:
    2.70 +				break
    2.71 +		return warnings
    2.72 +
    2.73 +	def get_optimization_flags(self, level):
    2.74 +		if level == 0:
    2.75 +			return ['-O0']
    2.76 +		elif level == 1:
    2.77 +			return ['-O']
    2.78 +		elif level == 2:
    2.79 +			return ['-O2']
    2.80 +		elif level == 3:
    2.81 +			return ['-O3']
    2.82 +
    2.83 +	def get_debug_flags(self, level):
    2.84 +		if level == 0:
    2.85 +			return (['-g0'], ['NDEBUG'])
    2.86 +		elif level == 1:
    2.87 +			return (['-g'], [])
    2.88 +		elif level >= 2:
    2.89 +			return (['-ggdb', '-g3'], ['_DEBUG'])
    2.90 +		
    2.91 +
    2.92 +
    2.93 +class MsvcTraits(CompilerTraits):
    2.94 +	def __init__(self):
    2.95 +		super(MsvcTraits, self).__init__()
    2.96 +		# cumulative list of warnings per level
    2.97 +		self.warnings_flags = [['/W2'], ['/WX'], ['/Wall']]
    2.98 +
    2.99 +	def get_warnings_flags(self, level):
   2.100 +		warnings = []
   2.101 +		for l in range(level):
   2.102 +			if l < len(self.warnings_flags):
   2.103 +				warnings.extend(self.warnings_flags[l])
   2.104 +			else:
   2.105 +				break
   2.106 +		return warnings
   2.107 +
   2.108 +	def get_optimization_flags(self, level):
   2.109 +		if level == 0:
   2.110 +			return ['/Od']
   2.111 +		elif level == 1:
   2.112 +			return []
   2.113 +		elif level == 2:
   2.114 +			return ['/O2']
   2.115 +		elif level == 3:
   2.116 +			return ['/Ox']
   2.117 +
   2.118 +	def get_debug_flags(self, level):
   2.119 +		if level == 0:
   2.120 +			return ([], ['NDEBUG'])
   2.121 +		elif level == 1:
   2.122 +			return (['/ZI', '/RTC1'], [])
   2.123 +		elif level >= 2:
   2.124 +			return (['/ZI', '/RTC1'], ['_DEBUG'])
   2.125 +
   2.126 +
   2.127 +
   2.128 +gcc = GccTraits()
   2.129 +icc = IccTraits()
   2.130 +msvc = MsvcTraits()
   2.131 +
   2.132 +# how to map env['COMPILER_CC'] or env['COMPILER_CXX'] into a traits object
   2.133 +compiler_mapping = {
   2.134 +	'gcc': gcc,
   2.135 +	'g++': gcc,
   2.136 +	'msvc': msvc,
   2.137 +	'icc': icc,
   2.138 +	'icpc': icc,
   2.139 +}
   2.140 +
   2.141 +profiles = {
   2.142 +	# profile name: [optimization_level, warnings_level, debug_level]
   2.143 +	'default': [2, 1, 1],
   2.144 +	'debug': [0, 2, 3],
   2.145 +	'release': [3, 1, 0],
   2.146 +	}
   2.147 +
   2.148 +default_profile = 'default'
   2.149 +
   2.150 +def set_options(opt):
   2.151 +	assert default_profile in profiles
   2.152 +	opt.add_option('-d', '--build-profile',
   2.153 +		       action='store',
   2.154 +		       default=default_profile,
   2.155 +		       help=("Specify the build profile.  "
   2.156 +			     "Build profiles control the default compilation flags"
   2.157 +			     " used for C/C++ programs, if CCFLAGS/CXXFLAGS are not"
   2.158 +			     " set set in the environment. [Allowed Values: %s]"
   2.159 +			     % ", ".join([repr(p) for p in profiles.keys()])),
   2.160 +		       choices=profiles.keys(),
   2.161 +		       dest='build_profile')
   2.162 +
   2.163 +def detect(conf):
   2.164 +	cc = conf.env['COMPILER_CC'] or None
   2.165 +	cxx = conf.env['COMPILER_CXX'] or None
   2.166 +	if not (cc or cxx):
   2.167 +		raise Utils.WafError("neither COMPILER_CC nor COMPILER_CXX are defined; "
   2.168 +				     "maybe the compiler_cc or compiler_cxx tool has not been configured yet?")
   2.169 +	
   2.170 +	try:
   2.171 +		compiler = compiler_mapping[cc]
   2.172 +	except KeyError:
   2.173 +		try:
   2.174 +			compiler = compiler_mapping[cxx]
   2.175 +		except KeyError:
   2.176 +			Logs.warn("No compiler flags support for compiler %r or %r"
   2.177 +				  % (cc, cxx))
   2.178 +			return
   2.179 +
   2.180 +	opt_level, warn_level, dbg_level = profiles[Options.options.build_profile]
   2.181 +
   2.182 +	optimizations = compiler.get_optimization_flags(opt_level)
   2.183 +	debug, debug_defs = compiler.get_debug_flags(dbg_level)
   2.184 +	warnings = compiler.get_warnings_flags(warn_level)
   2.185 +	
   2.186 +	if cc and not conf.env['CCFLAGS']:
   2.187 +		conf.env.append_value('CCFLAGS', optimizations)
   2.188 +		conf.env.append_value('CCFLAGS', debug)
   2.189 +		conf.env.append_value('CCFLAGS', warnings)
   2.190 +		conf.env.append_value('CCDEFINES', debug_defs)
   2.191 +	if cxx and not conf.env['CXXFLAGS']:
   2.192 +		conf.env.append_value('CXXFLAGS', optimizations)
   2.193 +		conf.env.append_value('CXXFLAGS', debug)
   2.194 +		conf.env.append_value('CXXFLAGS', warnings)
   2.195 +		conf.env.append_value('CXXDEFINES', debug_defs)
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/waf-tools/command.py	Fri Oct 23 13:05:56 2009 +0100
     3.3 @@ -0,0 +1,134 @@
     3.4 +from TaskGen import feature, taskgen, before, task_gen
     3.5 +import Node, Task, Utils, Build, pproc, Constants
     3.6 +import Options
     3.7 +
     3.8 +import shellcmd
     3.9 +shellcmd.subprocess = pproc # the WAF version of the subprocess module is supposedly less buggy
    3.10 +
    3.11 +from Logs import debug, error
    3.12 +shellcmd.debug = debug
    3.13 +
    3.14 +import Task
    3.15 +
    3.16 +import re
    3.17 +
    3.18 +
    3.19 +arg_rx = re.compile(r"(?P<dollar>\$\$)|(?P<subst>\$\{(?P<var>\w+)(?P<code>.*?)\})", re.M)
    3.20 +
    3.21 +class command_task(Task.Task):
    3.22 +	color = "BLUE"
    3.23 +	def __init__(self, env, generator):
    3.24 +		Task.Task.__init__(self, env, normal=1, generator=generator)
    3.25 +
    3.26 +	def __str__(self):
    3.27 +		"string to display to the user"
    3.28 +		env = self.env
    3.29 +		src_str = ' '.join([a.nice_path(env) for a in self.inputs])
    3.30 +		tgt_str = ' '.join([a.nice_path(env) for a in self.outputs])
    3.31 +		if self.outputs:
    3.32 +			sep = ' -> '
    3.33 +		else:
    3.34 +			sep = ''
    3.35 +
    3.36 +		pipeline = shellcmd.Pipeline()
    3.37 +		pipeline.parse(self.generator.command)
    3.38 +		cmd = pipeline.get_abbreviated_command()
    3.39 +
    3.40 +		return 'command (%s): %s%s%s\n' % (cmd, src_str, sep, tgt_str)
    3.41 +
    3.42 +	def _subst_arg(self, arg, direction, namespace):
    3.43 +		"""
    3.44 +		@param arg: the command argument (or stdin/stdout/stderr) to substitute
    3.45 +		@param direction: direction of the argument: 'in', 'out', or None
    3.46 +		"""
    3.47 +		def repl(match):
    3.48 +			if match.group('dollar'):
    3.49 +				return "$"
    3.50 +			elif match.group('subst'):
    3.51 +				var = match.group('var')
    3.52 +				code = match.group('code')
    3.53 +				result = eval(var+code, namespace)
    3.54 +				if isinstance(result, Node.Node):
    3.55 +					if var == 'TGT':
    3.56 +						return result.bldpath(self.env)
    3.57 +					elif var == 'SRC':
    3.58 +						return result.srcpath(self.env)
    3.59 +					else:
    3.60 +						raise ValueError("Bad subst variable %r" % var)
    3.61 +				elif result is self.inputs:
    3.62 +					if len(self.inputs) == 1:
    3.63 +						return result[0].srcpath(self.env)
    3.64 +					else:
    3.65 +						raise ValueError("${SRC} requested but have multiple sources; which one?")
    3.66 +				elif result is self.outputs:
    3.67 +					if len(self.outputs) == 1:
    3.68 +						return result[0].bldpath(self.env)
    3.69 +					else:
    3.70 +						raise ValueError("${TGT} requested but have multiple targets; which one?")
    3.71 +				else:
    3.72 +					return result
    3.73 +			return None
    3.74 +
    3.75 +		return arg_rx.sub(repl, arg)
    3.76 +
    3.77 +	def run(self):
    3.78 +		pipeline = shellcmd.Pipeline()
    3.79 +		pipeline.parse(self.generator.command)
    3.80 +		namespace = self.env.get_merged_dict()
    3.81 +		if self.generator.variables is not None:
    3.82 +			namespace.update(self.generator.variables)
    3.83 +		namespace.update(env=self.env, SRC=self.inputs, TGT=self.outputs)
    3.84 +		for cmd in pipeline.pipeline:
    3.85 +			if isinstance(cmd, shellcmd.Command):
    3.86 +				if isinstance(cmd.stdin, basestring):
    3.87 +					cmd.stdin = self._subst_arg(cmd.stdin, 'in', namespace)
    3.88 +				if isinstance(cmd.stdout, basestring):
    3.89 +					cmd.stdout = self._subst_arg(cmd.stdout, 'out', namespace)
    3.90 +				if isinstance(cmd.stderr, basestring):
    3.91 +					cmd.stderr = self._subst_arg(cmd.stderr, 'out', namespace)
    3.92 +				for argI in xrange(len(cmd.argv)):
    3.93 +					cmd.argv[argI] = self._subst_arg(cmd.argv[argI], None, namespace)
    3.94 +				if cmd.env_vars is not None:
    3.95 +					env_vars = dict()
    3.96 +					for name, value in cmd.env_vars.iteritems():
    3.97 +						env_vars[name] = self._subst_arg(value, None, namespace)
    3.98 +					cmd.env_vars = env_vars
    3.99 +			elif isinstance(cmd, shellcmd.Chdir):
   3.100 +				cmd.dir = self._subst_arg(cmd.dir, None, namespace)
   3.101 +
   3.102 +		return pipeline.run(verbose=(Options.options.verbose > 0))
   3.103 +
   3.104 +@taskgen
   3.105 +@feature('command')
   3.106 +def init_command(self):
   3.107 +	Utils.def_attrs(self,
   3.108 +			# other variables that can be used in the command: ${VARIABLE}
   3.109 +			variables = None)
   3.110 +
   3.111 +
   3.112 +
   3.113 +@taskgen
   3.114 +@feature('command')
   3.115 +@before('apply_core')
   3.116 +def apply_command(self):
   3.117 +	self.meths.remove('apply_core')
   3.118 +	# create the task
   3.119 +	task = self.create_task('command')
   3.120 +	setattr(task, "dep_vars", getattr(self, "dep_vars", None))
   3.121 +	# process the sources
   3.122 +	inputs = []
   3.123 +	for src in self.to_list(self.source):
   3.124 +		node = self.path.find_resource(src)
   3.125 +		if node is None:
   3.126 +			raise Utils.WafError("source %s not found" % src)
   3.127 +                inputs.append(node)
   3.128 +	task.set_inputs(inputs)
   3.129 +	task.set_outputs([self.path.find_or_declare(tgt) for tgt in self.to_list(self.target)])
   3.130 +	#Task.file_deps = Task.extract_deps
   3.131 +
   3.132 +
   3.133 +
   3.134 +class command_taskgen(task_gen):
   3.135 +	def __init__(self, *k, **kw):
   3.136 +		task_gen.__init__(self, *k, **kw)
   3.137 +		self.features.append('command')
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/waf-tools/pkgconfig.py	Fri Oct 23 13:05:56 2009 +0100
     4.3 @@ -0,0 +1,71 @@
     4.4 +# -*- mode: python; encoding: utf-8 -*-
     4.5 +# Gustavo Carneiro (gjamc) 2008
     4.6 +
     4.7 +import Options
     4.8 +import Configure
     4.9 +import pproc as subprocess
    4.10 +import config_c
    4.11 +
    4.12 +def detect(conf):
    4.13 +	pkg_config = conf.find_program('pkg-config', var='PKG_CONFIG')
    4.14 +	if not pkg_config: return
    4.15 +
    4.16 +@Configure.conf
    4.17 +def pkg_check_modules(conf, uselib_name, expression, mandatory=True):
    4.18 +	pkg_config = conf.env['PKG_CONFIG']
    4.19 +	if not pkg_config:
    4.20 +		if mandatory:
    4.21 +			conf.fatal("pkg-config is not available")
    4.22 +		else:
    4.23 +			return False
    4.24 +
    4.25 +	argv = [pkg_config, '--cflags', '--libs', expression]
    4.26 +	cmd = subprocess.Popen(argv, stdout=subprocess.PIPE)
    4.27 +	out, dummy = cmd.communicate()
    4.28 +	retval = cmd.wait()
    4.29 +
    4.30 +	msg_checking = ("pkg-config flags for %s" % (uselib_name,))
    4.31 +	if Options.options.verbose:
    4.32 +		if retval == 0:
    4.33 +			conf.check_message_custom(msg_checking,
    4.34 +						  ('(%s)' % expression), out)
    4.35 +		else:
    4.36 +			conf.check_message(msg_checking, ('(%s)' % expression), False)
    4.37 +	else:
    4.38 +		conf.check_message(msg_checking, '', (retval == 0), '')
    4.39 +	conf.log.write('%r: %r (exit code %i)\n' % (argv, out, retval))
    4.40 +
    4.41 +	if retval == 0:
    4.42 +
    4.43 +		config_c.parse_flags(out, uselib_name, conf.env)
    4.44 +		conf.env[uselib_name] = True
    4.45 +		return True
    4.46 +
    4.47 +	else:
    4.48 +
    4.49 +		conf.env[uselib_name] = False
    4.50 +		if mandatory:
    4.51 +			raise Configure.ConfigurationError('pkg-config check failed')
    4.52 +		else:
    4.53 +			return False
    4.54 +
    4.55 +@Configure.conf
    4.56 +def pkg_check_module_variable(conf, module, variable):
    4.57 +	pkg_config = conf.env['PKG_CONFIG']
    4.58 +	if not pkg_config:
    4.59 +		conf.fatal("pkg-config is not available")
    4.60 +
    4.61 +	argv = [pkg_config, '--variable', variable, module]
    4.62 +	cmd = subprocess.Popen(argv, stdout=subprocess.PIPE)
    4.63 +	out, dummy = cmd.communicate()
    4.64 +	retval = cmd.wait()
    4.65 +	out = out.rstrip() # strip the trailing newline
    4.66 +
    4.67 +	msg_checking = ("pkg-config variable %r in %s" % (variable, module,))
    4.68 +	conf.check_message_custom(msg_checking, '', out)
    4.69 +	conf.log.write('%r: %r (exit code %i)\n' % (argv, out, retval))
    4.70 +
    4.71 +	if retval == 0:
    4.72 +		return out
    4.73 +	else:
    4.74 +		raise Configure.ConfigurationError('pkg-config check failed')
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/waf-tools/shellcmd.py	Fri Oct 23 13:05:56 2009 +0100
     5.3 @@ -0,0 +1,345 @@
     5.4 +# Copyright (C) 2008 Gustavo J. A. M. Carneiro  <gjcarneiro@gmail.com>
     5.5 +
     5.6 +# This program is free software; you can redistribute it and/or modify
     5.7 +# it under the terms of the GNU General Public License as published by
     5.8 +# the Free Software Foundation; either version 2 of the License, or
     5.9 +# (at your option) any later version.
    5.10 +
    5.11 +# This program is distributed in the hope that it will be useful,
    5.12 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.13 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.14 +# GNU General Public License for more details.
    5.15 +
    5.16 +# You should have received a copy of the GNU General Public License
    5.17 +# along with this program; if not, write to the Free Software
    5.18 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    5.19 +
    5.20 +import shlex
    5.21 +import subprocess
    5.22 +import sys
    5.23 +import re
    5.24 +import os
    5.25 +
    5.26 +env_var_rx = re.compile(r"^([a-zA-Z0-9_]+)=(\S+)$")
    5.27 +
    5.28 +def debug(message):
    5.29 +    print >> sys.stderr, message
    5.30 +
    5.31 +
    5.32 +if sys.platform == 'win32':
    5.33 +    dev_null = open("NUL:", "w")
    5.34 +else:
    5.35 +    dev_null = open("/dev/null", "w")
    5.36 +
    5.37 +def _open_out_file(filename):
    5.38 +    if filename in ['NUL:', '/dev/null']:
    5.39 +        return dev_null
    5.40 +    else:
    5.41 +        return open(filename, 'wb')
    5.42 +
    5.43 +
    5.44 +class Node(object):
    5.45 +    pass
    5.46 +
    5.47 +class Op(Node):
    5.48 +    pass
    5.49 +
    5.50 +class Pipe(Op):
    5.51 +    pass
    5.52 +
    5.53 +class And(Op):
    5.54 +    pass
    5.55 +
    5.56 +class Or(Op):
    5.57 +    pass
    5.58 +
    5.59 +class Command(Node):
    5.60 +    class PIPE(object):
    5.61 +        pass # PIPE is a constant
    5.62 +    class STDOUT(object):
    5.63 +        pass # PIPE is a constant
    5.64 +
    5.65 +    def __init__(self, name):
    5.66 +        super(Command, self).__init__()
    5.67 +        self.name = name # command name
    5.68 +        self.argv = [name] # command argv
    5.69 +        self.stdin = None
    5.70 +        self.stdout = None
    5.71 +        self.stderr = None
    5.72 +        self.env_vars = None
    5.73 +
    5.74 +    def __repr__(self):
    5.75 +        return "Command(%r, argv=%r, stdin=%r, stdout=%r, stderr=%r)" \
    5.76 +            % (self.name, self.argv, self.stdin, self.stdout, self.stderr)
    5.77 +
    5.78 +class Chdir(Node):
    5.79 +    def __init__(self):
    5.80 +        super(Chdir, self).__init__()
    5.81 +        self.dir = None
    5.82 +
    5.83 +    def __repr__(self):
    5.84 +        return "Chdir(%r)" \
    5.85 +            % (self.dir)
    5.86 +
    5.87 +class Pipeline(object):
    5.88 +    def __init__(self):
    5.89 +        self.current_command = None
    5.90 +        self.pipeline = []
    5.91 +
    5.92 +    def _commit_command(self):
    5.93 +        assert self.current_command is not None
    5.94 +        self.pipeline.append(self.current_command)
    5.95 +        self.current_command = None
    5.96 +
    5.97 +    def get_abbreviated_command(self):
    5.98 +        l = []
    5.99 +        for node in self.pipeline:
   5.100 +            if isinstance(node, Command):
   5.101 +                l.append(node.name)
   5.102 +            if isinstance(node, Chdir):
   5.103 +                l.append('cd %s' % node.dir)
   5.104 +            elif isinstance(node, Pipe):
   5.105 +                l.append('|')
   5.106 +            elif isinstance(node, And):
   5.107 +                l.append('&&')
   5.108 +            elif isinstance(node, And):
   5.109 +                l.append('||')
   5.110 +        return ' '.join(l)
   5.111 +
   5.112 +    def parse(self, command):
   5.113 +        self.current_command = None
   5.114 +        self.pipeline = []
   5.115 +
   5.116 +        if isinstance(command, list):
   5.117 +            tokens = list(command)
   5.118 +        else:
   5.119 +            tokens = shlex.split(command)
   5.120 +        debug("command: shlex: %r" % (tokens,))
   5.121 +
   5.122 +        BEGIN, COMMAND, CHDIR, STDERR, STDOUT, STDIN = range(6)
   5.123 +        state = BEGIN
   5.124 +        self.current_command = None
   5.125 +        env_vars = dict()
   5.126 +
   5.127 +        while tokens:
   5.128 +            token = tokens.pop(0)
   5.129 +            if state == BEGIN:
   5.130 +                env_var_match = env_var_rx.match(token)
   5.131 +                if env_var_match is not None:
   5.132 +                    env_vars[env_var_match.group(1)] = env_var_match.group(2)
   5.133 +                else:
   5.134 +                    assert self.current_command is None
   5.135 +                    if token == 'cd':
   5.136 +                        self.current_command = Chdir()
   5.137 +                        assert not env_vars
   5.138 +                        state = CHDIR
   5.139 +                    else:
   5.140 +                        self.current_command = Command(token)
   5.141 +                        if env_vars:
   5.142 +                            self.current_command.env_vars = env_vars
   5.143 +                            env_vars = dict()
   5.144 +                        state = COMMAND
   5.145 +            elif state == COMMAND:
   5.146 +                if token == '>':
   5.147 +                    state = STDOUT
   5.148 +                elif token == '2>':
   5.149 +                    state = STDERR
   5.150 +                elif token == '2>&1':
   5.151 +                    assert self.current_command.stderr is None
   5.152 +                    self.current_command.stderr = Command.STDOUT
   5.153 +                elif token == '<':
   5.154 +                    state = STDIN
   5.155 +                elif token == '|':
   5.156 +                    assert self.current_command.stdout is None
   5.157 +                    self.current_command.stdout = Command.PIPE
   5.158 +                    self._commit_command()
   5.159 +                    self.pipeline.append(Pipe())
   5.160 +                    state = BEGIN
   5.161 +                elif token == '&&':
   5.162 +                    self._commit_command()
   5.163 +                    self.pipeline.append(And())
   5.164 +                    state = BEGIN
   5.165 +                elif token == '||':
   5.166 +                    self._commit_command()
   5.167 +                    self.pipeline.append(Or())
   5.168 +                    state = BEGIN
   5.169 +                else:
   5.170 +                    self.current_command.argv.append(token)
   5.171 +            elif state == CHDIR:
   5.172 +                if token == '&&':
   5.173 +                    self._commit_command()
   5.174 +                    self.pipeline.append(And())
   5.175 +                    state = BEGIN
   5.176 +                else:
   5.177 +                    assert self.current_command.dir is None
   5.178 +                    self.current_command.dir = token
   5.179 +            elif state == STDOUT:
   5.180 +                assert self.current_command.stdout is None
   5.181 +                self.current_command.stdout = token
   5.182 +                state = COMMAND
   5.183 +            elif state == STDERR:
   5.184 +                assert self.current_command.stderr is None
   5.185 +                self.current_command.stderr = token
   5.186 +                state = COMMAND
   5.187 +            elif state == STDIN:
   5.188 +                assert self.current_command.stdin is None
   5.189 +                self.current_command.stdin = token
   5.190 +                state = COMMAND
   5.191 +        self._commit_command()
   5.192 +        return self.pipeline
   5.193 +
   5.194 +    def _exec_piped_commands(self, commands):
   5.195 +        retvals = []
   5.196 +        for cmd in commands:
   5.197 +            retvals.append(cmd.wait())
   5.198 +        retval = 0
   5.199 +        for r in retvals:
   5.200 +            if r:
   5.201 +                retval = retvals[-1]
   5.202 +                break
   5.203 +        return retval
   5.204 +
   5.205 +    def run(self, verbose=False):
   5.206 +        pipeline = list(self.pipeline)
   5.207 +        files_to_close = []
   5.208 +        piped_commands = []
   5.209 +        piped_commands_display = []
   5.210 +        BEGIN, PIPE = range(2)
   5.211 +        state = BEGIN
   5.212 +        cwd = '.'
   5.213 +        while pipeline:
   5.214 +            node = pipeline.pop(0)
   5.215 +
   5.216 +            if isinstance(node, Chdir):
   5.217 +                next_op = pipeline.pop(0)
   5.218 +                assert isinstance(next_op, And)
   5.219 +                cwd = os.path.join(cwd, node.dir)
   5.220 +                if verbose:
   5.221 +                    piped_commands_display.append("cd %s &&" % node.dir)
   5.222 +                continue
   5.223 +            
   5.224 +            assert isinstance(node, (Command, Chdir))
   5.225 +            cmd = node
   5.226 +            if verbose:
   5.227 +                if cmd.env_vars:
   5.228 +                    env_vars_str = ' '.join(['%s=%s' % (key, val) for key, val in cmd.env_vars.iteritems()])
   5.229 +                    piped_commands_display.append("%s %s" % (env_vars_str, ' '.join(cmd.argv)))
   5.230 +                else:
   5.231 +                    piped_commands_display.append(' '.join(cmd.argv))
   5.232 +
   5.233 +            if state == PIPE:
   5.234 +                stdin = piped_commands[-1].stdout
   5.235 +            elif cmd.stdin is not None:
   5.236 +                stdin = open(cmd.stdin, "r")
   5.237 +                if verbose:
   5.238 +                    piped_commands_display.append('< %s' % cmd.stdin)
   5.239 +                files_to_close.append(stdin)
   5.240 +            else:
   5.241 +                stdin = None
   5.242 +
   5.243 +            if cmd.stdout is None:
   5.244 +                stdout = None
   5.245 +            elif cmd.stdout is Command.PIPE:
   5.246 +                stdout = subprocess.PIPE
   5.247 +            else:
   5.248 +                stdout = _open_out_file(cmd.stdout)
   5.249 +                files_to_close.append(stdout)
   5.250 +                if verbose:
   5.251 +                    piped_commands_display.append('> %s' % cmd.stdout)
   5.252 +
   5.253 +            if cmd.stderr is None:
   5.254 +                stderr = None
   5.255 +            elif cmd.stderr is Command.PIPE:
   5.256 +                stderr = subprocess.PIPE
   5.257 +            elif cmd.stderr is Command.STDOUT:
   5.258 +                stderr = subprocess.STDOUT
   5.259 +                if verbose:
   5.260 +                    piped_commands_display.append('2>&1')
   5.261 +            else:
   5.262 +                stderr = _open_out_file(cmd.stderr)
   5.263 +                files_to_close.append(stderr)
   5.264 +                if verbose:
   5.265 +                    piped_commands_display.append('2> %s' % cmd.stderr)
   5.266 +
   5.267 +            if cmd.env_vars:
   5.268 +                env = dict(os.environ)
   5.269 +                env.update(cmd.env_vars)
   5.270 +            else:
   5.271 +                env = None
   5.272 +
   5.273 +            if cwd == '.':
   5.274 +                proc_cwd = None
   5.275 +            else:
   5.276 +                proc_cwd = cwd
   5.277 +
   5.278 +            debug("command: subprocess.Popen(argv=%r, stdin=%r, stdout=%r, stderr=%r, env_vars=%r, cwd=%r)"
   5.279 +                  % (cmd.argv, stdin, stdout, stderr, cmd.env_vars, proc_cwd))
   5.280 +            proc = subprocess.Popen(cmd.argv, stdin=stdin, stdout=stdout, stderr=stderr, env=env, cwd=proc_cwd)
   5.281 +            del stdin, stdout, stderr
   5.282 +            piped_commands.append(proc)
   5.283 +
   5.284 +            try:
   5.285 +                next_node = pipeline.pop(0)
   5.286 +            except IndexError:
   5.287 +                try:
   5.288 +                    retval = self._exec_piped_commands(piped_commands)
   5.289 +                    if verbose:
   5.290 +                        print "%s: exit code %i" % (' '.join(piped_commands_display), retval)
   5.291 +                finally:
   5.292 +                    for f in files_to_close:
   5.293 +                        if f is not dev_null:
   5.294 +                            f.close()
   5.295 +                    files_to_close = []
   5.296 +                return retval
   5.297 +            else:
   5.298 +
   5.299 +                if isinstance(next_node, Pipe):
   5.300 +                    state = PIPE
   5.301 +                    piped_commands_display.append('|')
   5.302 +
   5.303 +                elif isinstance(next_node, Or):
   5.304 +                    try:
   5.305 +                        this_retval = self._exec_piped_commands(piped_commands)
   5.306 +                    finally:
   5.307 +                        for f in files_to_close:
   5.308 +                            if f is not dev_null:
   5.309 +                                f.close()
   5.310 +                        files_to_close = []
   5.311 +                    if this_retval == 0:
   5.312 +                        if verbose:
   5.313 +                            print "%s: exit code %i (|| is short-circuited)" % (' '.join(piped_commands_display), retval)
   5.314 +                        return this_retval
   5.315 +                    if verbose:
   5.316 +                        print "%s: exit code %i (|| proceeds)" % (' '.join(piped_commands_display), retval)
   5.317 +                    state = BEGIN
   5.318 +                    piped_commands = []
   5.319 +                    piped_commands_display = []
   5.320 +
   5.321 +                elif isinstance(next_node, And):
   5.322 +                    try:
   5.323 +                        this_retval = self._exec_piped_commands(piped_commands)
   5.324 +                    finally:
   5.325 +                        for f in files_to_close:
   5.326 +                            if f is not dev_null:
   5.327 +                                f.close()
   5.328 +                        files_to_close = []
   5.329 +                    if this_retval != 0:
   5.330 +                        if verbose:
   5.331 +                            print "%s: exit code %i (&& is short-circuited)" % (' '.join(piped_commands_display), retval)
   5.332 +                        return this_retval
   5.333 +                    if verbose:
   5.334 +                        print "%s: exit code %i (&& proceeds)" % (' '.join(piped_commands_display), retval)
   5.335 +                    state = BEGIN
   5.336 +                    piped_commands = []
   5.337 +                    piped_commands_display = []
   5.338 +
   5.339 +
   5.340 +
   5.341 +def _main():
   5.342 +    pipeline = Pipeline()
   5.343 +    pipeline.parse('./foo.py 2>&1 < xxx | cat && ls')
   5.344 +    print pipeline.run()
   5.345 +
   5.346 +if __name__ == '__main__':
   5.347 +    _main()
   5.348 +
     6.1 --- a/wscript	Fri Oct 23 15:34:14 2009 +0400
     6.2 +++ b/wscript	Fri Oct 23 13:05:56 2009 +0100
     6.3 @@ -27,6 +27,8 @@
     6.4  import Configure
     6.5  import Scripting
     6.6  
     6.7 +sys.path.insert(0, os.path.abspath('waf-tools'))
     6.8 +
     6.9  import cflags # override the build profiles from waf
    6.10  cflags.profiles = {
    6.11  	# profile name: [optimization_level, warnings_level, debug_level]