Merge
authorKirill Andreev <andreev@iitp.ru>
Fri Oct 23 16:23:38 2009 +0400 (3 months ago)
changeset 5497a1680cbd70e9
parent 5496 dfcd6882cf19
parent 5495 a9678e91ec7d
child 5498 15524c57a627
Merge
     1.1 --- a/doc/build.txt	Fri Oct 23 16:22:56 2009 +0400
     1.2 +++ b/doc/build.txt	Fri Oct 23 16:23:38 2009 +0400
     1.3 @@ -7,10 +7,10 @@
     1.4  
     1.5  === Installing Waf ===
     1.6  
     1.7 -The top-level ns-3 directory should contain a current waf script.
     1.8 -
     1.9 -Note: we're using a WAF version based on WAF 1.5.x.  The source code
    1.10 -can be retrieved from the followin URL:
    1.11 +The top-level ns-3 directory should contain a current waf script, so
    1.12 +there is no need to have WAF installed in the system.  We are using
    1.13 +some extensions to WAF, which can be found in the 'waf-tools'
    1.14 +directory. The upstream location for these WAF extensions is:
    1.15  
    1.16      https://code.launchpad.net/~gjc/waf/cmd
    1.17  
     2.1 Binary file waf has changed
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/waf-tools/cflags.py	Fri Oct 23 16:23:38 2009 +0400
     3.3 @@ -0,0 +1,192 @@
     3.4 +import Logs
     3.5 +import Options
     3.6 +import Utils
     3.7 +
     3.8 +
     3.9 +class CompilerTraits(object):
    3.10 +	def get_warnings_flags(self, level):
    3.11 +		"""get_warnings_flags(level) -> list of cflags"""
    3.12 +		raise NotImplementedError
    3.13 +
    3.14 +	def get_optimization_flags(self, level):
    3.15 +		"""get_optimization_flags(level) -> list of cflags"""
    3.16 +		raise NotImplementedError
    3.17 +
    3.18 +	def get_debug_flags(self, level):
    3.19 +		"""get_debug_flags(level) -> (list of cflags, list of cppdefines)"""
    3.20 +		raise NotImplementedError
    3.21 +
    3.22 +
    3.23 +class GccTraits(CompilerTraits):
    3.24 +	def __init__(self):
    3.25 +		super(GccTraits, self).__init__()
    3.26 +		# cumulative list of warnings per level
    3.27 +		self.warnings_flags = [['-Wall'], ['-Werror'], ['-Wextra']]
    3.28 +
    3.29 +	def get_warnings_flags(self, level):
    3.30 +		warnings = []
    3.31 +		for l in range(level):
    3.32 +			if l < len(self.warnings_flags):
    3.33 +				warnings.extend(self.warnings_flags[l])
    3.34 +			else:
    3.35 +				break
    3.36 +		return warnings
    3.37 +
    3.38 +	def get_optimization_flags(self, level):
    3.39 +		if level == 0:
    3.40 +			return ['-O0']
    3.41 +		elif level == 1:
    3.42 +			return ['-O']
    3.43 +		elif level == 2:
    3.44 +			return ['-O2']
    3.45 +		elif level == 3:
    3.46 +			return ['-O3']
    3.47 +
    3.48 +	def get_debug_flags(self, level):
    3.49 +		if level == 0:
    3.50 +			return (['-g0'], ['NDEBUG'])
    3.51 +		elif level == 1:
    3.52 +			return (['-g'], [])
    3.53 +		elif level >= 2:
    3.54 +			return (['-ggdb', '-g3'], ['_DEBUG'])
    3.55 +		
    3.56 +
    3.57 +class IccTraits(CompilerTraits):
    3.58 +	def __init__(self):
    3.59 +		super(IccTraits, self).__init__()
    3.60 +		# cumulative list of warnings per level
    3.61 +		# icc is _very_ verbose with -Wall, -Werror is barely achievable
    3.62 +		self.warnings_flags = [[], [], ['-Wall']]
    3.63 +		
    3.64 +	def get_warnings_flags(self, level):
    3.65 +		warnings = []
    3.66 +		for l in range(level):
    3.67 +			if l < len(self.warnings_flags):
    3.68 +				warnings.extend(self.warnings_flags[l])
    3.69 +			else:
    3.70 +				break
    3.71 +		return warnings
    3.72 +
    3.73 +	def get_optimization_flags(self, level):
    3.74 +		if level == 0:
    3.75 +			return ['-O0']
    3.76 +		elif level == 1:
    3.77 +			return ['-O']
    3.78 +		elif level == 2:
    3.79 +			return ['-O2']
    3.80 +		elif level == 3:
    3.81 +			return ['-O3']
    3.82 +
    3.83 +	def get_debug_flags(self, level):
    3.84 +		if level == 0:
    3.85 +			return (['-g0'], ['NDEBUG'])
    3.86 +		elif level == 1:
    3.87 +			return (['-g'], [])
    3.88 +		elif level >= 2:
    3.89 +			return (['-ggdb', '-g3'], ['_DEBUG'])
    3.90 +		
    3.91 +
    3.92 +
    3.93 +class MsvcTraits(CompilerTraits):
    3.94 +	def __init__(self):
    3.95 +		super(MsvcTraits, self).__init__()
    3.96 +		# cumulative list of warnings per level
    3.97 +		self.warnings_flags = [['/W2'], ['/WX'], ['/Wall']]
    3.98 +
    3.99 +	def get_warnings_flags(self, level):
   3.100 +		warnings = []
   3.101 +		for l in range(level):
   3.102 +			if l < len(self.warnings_flags):
   3.103 +				warnings.extend(self.warnings_flags[l])
   3.104 +			else:
   3.105 +				break
   3.106 +		return warnings
   3.107 +
   3.108 +	def get_optimization_flags(self, level):
   3.109 +		if level == 0:
   3.110 +			return ['/Od']
   3.111 +		elif level == 1:
   3.112 +			return []
   3.113 +		elif level == 2:
   3.114 +			return ['/O2']
   3.115 +		elif level == 3:
   3.116 +			return ['/Ox']
   3.117 +
   3.118 +	def get_debug_flags(self, level):
   3.119 +		if level == 0:
   3.120 +			return ([], ['NDEBUG'])
   3.121 +		elif level == 1:
   3.122 +			return (['/ZI', '/RTC1'], [])
   3.123 +		elif level >= 2:
   3.124 +			return (['/ZI', '/RTC1'], ['_DEBUG'])
   3.125 +
   3.126 +
   3.127 +
   3.128 +gcc = GccTraits()
   3.129 +icc = IccTraits()
   3.130 +msvc = MsvcTraits()
   3.131 +
   3.132 +# how to map env['COMPILER_CC'] or env['COMPILER_CXX'] into a traits object
   3.133 +compiler_mapping = {
   3.134 +	'gcc': gcc,
   3.135 +	'g++': gcc,
   3.136 +	'msvc': msvc,
   3.137 +	'icc': icc,
   3.138 +	'icpc': icc,
   3.139 +}
   3.140 +
   3.141 +profiles = {
   3.142 +	# profile name: [optimization_level, warnings_level, debug_level]
   3.143 +	'default': [2, 1, 1],
   3.144 +	'debug': [0, 2, 3],
   3.145 +	'release': [3, 1, 0],
   3.146 +	}
   3.147 +
   3.148 +default_profile = 'default'
   3.149 +
   3.150 +def set_options(opt):
   3.151 +	assert default_profile in profiles
   3.152 +	opt.add_option('-d', '--build-profile',
   3.153 +		       action='store',
   3.154 +		       default=default_profile,
   3.155 +		       help=("Specify the build profile.  "
   3.156 +			     "Build profiles control the default compilation flags"
   3.157 +			     " used for C/C++ programs, if CCFLAGS/CXXFLAGS are not"
   3.158 +			     " set set in the environment. [Allowed Values: %s]"
   3.159 +			     % ", ".join([repr(p) for p in profiles.keys()])),
   3.160 +		       choices=profiles.keys(),
   3.161 +		       dest='build_profile')
   3.162 +
   3.163 +def detect(conf):
   3.164 +	cc = conf.env['COMPILER_CC'] or None
   3.165 +	cxx = conf.env['COMPILER_CXX'] or None
   3.166 +	if not (cc or cxx):
   3.167 +		raise Utils.WafError("neither COMPILER_CC nor COMPILER_CXX are defined; "
   3.168 +				     "maybe the compiler_cc or compiler_cxx tool has not been configured yet?")
   3.169 +	
   3.170 +	try:
   3.171 +		compiler = compiler_mapping[cc]
   3.172 +	except KeyError:
   3.173 +		try:
   3.174 +			compiler = compiler_mapping[cxx]
   3.175 +		except KeyError:
   3.176 +			Logs.warn("No compiler flags support for compiler %r or %r"
   3.177 +				  % (cc, cxx))
   3.178 +			return
   3.179 +
   3.180 +	opt_level, warn_level, dbg_level = profiles[Options.options.build_profile]
   3.181 +
   3.182 +	optimizations = compiler.get_optimization_flags(opt_level)
   3.183 +	debug, debug_defs = compiler.get_debug_flags(dbg_level)
   3.184 +	warnings = compiler.get_warnings_flags(warn_level)
   3.185 +	
   3.186 +	if cc and not conf.env['CCFLAGS']:
   3.187 +		conf.env.append_value('CCFLAGS', optimizations)
   3.188 +		conf.env.append_value('CCFLAGS', debug)
   3.189 +		conf.env.append_value('CCFLAGS', warnings)
   3.190 +		conf.env.append_value('CCDEFINES', debug_defs)
   3.191 +	if cxx and not conf.env['CXXFLAGS']:
   3.192 +		conf.env.append_value('CXXFLAGS', optimizations)
   3.193 +		conf.env.append_value('CXXFLAGS', debug)
   3.194 +		conf.env.append_value('CXXFLAGS', warnings)
   3.195 +		conf.env.append_value('CXXDEFINES', debug_defs)
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/waf-tools/command.py	Fri Oct 23 16:23:38 2009 +0400
     4.3 @@ -0,0 +1,134 @@
     4.4 +from TaskGen import feature, taskgen, before, task_gen
     4.5 +import Node, Task, Utils, Build, pproc, Constants
     4.6 +import Options
     4.7 +
     4.8 +import shellcmd
     4.9 +shellcmd.subprocess = pproc # the WAF version of the subprocess module is supposedly less buggy
    4.10 +
    4.11 +from Logs import debug, error
    4.12 +shellcmd.debug = debug
    4.13 +
    4.14 +import Task
    4.15 +
    4.16 +import re
    4.17 +
    4.18 +
    4.19 +arg_rx = re.compile(r"(?P<dollar>\$\$)|(?P<subst>\$\{(?P<var>\w+)(?P<code>.*?)\})", re.M)
    4.20 +
    4.21 +class command_task(Task.Task):
    4.22 +	color = "BLUE"
    4.23 +	def __init__(self, env, generator):
    4.24 +		Task.Task.__init__(self, env, normal=1, generator=generator)
    4.25 +
    4.26 +	def __str__(self):
    4.27 +		"string to display to the user"
    4.28 +		env = self.env
    4.29 +		src_str = ' '.join([a.nice_path(env) for a in self.inputs])
    4.30 +		tgt_str = ' '.join([a.nice_path(env) for a in self.outputs])
    4.31 +		if self.outputs:
    4.32 +			sep = ' -> '
    4.33 +		else:
    4.34 +			sep = ''
    4.35 +
    4.36 +		pipeline = shellcmd.Pipeline()
    4.37 +		pipeline.parse(self.generator.command)
    4.38 +		cmd = pipeline.get_abbreviated_command()
    4.39 +
    4.40 +		return 'command (%s): %s%s%s\n' % (cmd, src_str, sep, tgt_str)
    4.41 +
    4.42 +	def _subst_arg(self, arg, direction, namespace):
    4.43 +		"""
    4.44 +		@param arg: the command argument (or stdin/stdout/stderr) to substitute
    4.45 +		@param direction: direction of the argument: 'in', 'out', or None
    4.46 +		"""
    4.47 +		def repl(match):
    4.48 +			if match.group('dollar'):
    4.49 +				return "$"
    4.50 +			elif match.group('subst'):
    4.51 +				var = match.group('var')
    4.52 +				code = match.group('code')
    4.53 +				result = eval(var+code, namespace)
    4.54 +				if isinstance(result, Node.Node):
    4.55 +					if var == 'TGT':
    4.56 +						return result.bldpath(self.env)
    4.57 +					elif var == 'SRC':
    4.58 +						return result.srcpath(self.env)
    4.59 +					else:
    4.60 +						raise ValueError("Bad subst variable %r" % var)
    4.61 +				elif result is self.inputs:
    4.62 +					if len(self.inputs) == 1:
    4.63 +						return result[0].srcpath(self.env)
    4.64 +					else:
    4.65 +						raise ValueError("${SRC} requested but have multiple sources; which one?")
    4.66 +				elif result is self.outputs:
    4.67 +					if len(self.outputs) == 1:
    4.68 +						return result[0].bldpath(self.env)
    4.69 +					else:
    4.70 +						raise ValueError("${TGT} requested but have multiple targets; which one?")
    4.71 +				else:
    4.72 +					return result
    4.73 +			return None
    4.74 +
    4.75 +		return arg_rx.sub(repl, arg)
    4.76 +
    4.77 +	def run(self):
    4.78 +		pipeline = shellcmd.Pipeline()
    4.79 +		pipeline.parse(self.generator.command)
    4.80 +		namespace = self.env.get_merged_dict()
    4.81 +		if self.generator.variables is not None:
    4.82 +			namespace.update(self.generator.variables)
    4.83 +		namespace.update(env=self.env, SRC=self.inputs, TGT=self.outputs)
    4.84 +		for cmd in pipeline.pipeline:
    4.85 +			if isinstance(cmd, shellcmd.Command):
    4.86 +				if isinstance(cmd.stdin, basestring):
    4.87 +					cmd.stdin = self._subst_arg(cmd.stdin, 'in', namespace)
    4.88 +				if isinstance(cmd.stdout, basestring):
    4.89 +					cmd.stdout = self._subst_arg(cmd.stdout, 'out', namespace)
    4.90 +				if isinstance(cmd.stderr, basestring):
    4.91 +					cmd.stderr = self._subst_arg(cmd.stderr, 'out', namespace)
    4.92 +				for argI in xrange(len(cmd.argv)):
    4.93 +					cmd.argv[argI] = self._subst_arg(cmd.argv[argI], None, namespace)
    4.94 +				if cmd.env_vars is not None:
    4.95 +					env_vars = dict()
    4.96 +					for name, value in cmd.env_vars.iteritems():
    4.97 +						env_vars[name] = self._subst_arg(value, None, namespace)
    4.98 +					cmd.env_vars = env_vars
    4.99 +			elif isinstance(cmd, shellcmd.Chdir):
   4.100 +				cmd.dir = self._subst_arg(cmd.dir, None, namespace)
   4.101 +
   4.102 +		return pipeline.run(verbose=(Options.options.verbose > 0))
   4.103 +
   4.104 +@taskgen
   4.105 +@feature('command')
   4.106 +def init_command(self):
   4.107 +	Utils.def_attrs(self,
   4.108 +			# other variables that can be used in the command: ${VARIABLE}
   4.109 +			variables = None)
   4.110 +
   4.111 +
   4.112 +
   4.113 +@taskgen
   4.114 +@feature('command')
   4.115 +@before('apply_core')
   4.116 +def apply_command(self):
   4.117 +	self.meths.remove('apply_core')
   4.118 +	# create the task
   4.119 +	task = self.create_task('command')
   4.120 +	setattr(task, "dep_vars", getattr(self, "dep_vars", None))
   4.121 +	# process the sources
   4.122 +	inputs = []
   4.123 +	for src in self.to_list(self.source):
   4.124 +		node = self.path.find_resource(src)
   4.125 +		if node is None:
   4.126 +			raise Utils.WafError("source %s not found" % src)
   4.127 +                inputs.append(node)
   4.128 +	task.set_inputs(inputs)
   4.129 +	task.set_outputs([self.path.find_or_declare(tgt) for tgt in self.to_list(self.target)])
   4.130 +	#Task.file_deps = Task.extract_deps
   4.131 +
   4.132 +
   4.133 +
   4.134 +class command_taskgen(task_gen):
   4.135 +	def __init__(self, *k, **kw):
   4.136 +		task_gen.__init__(self, *k, **kw)
   4.137 +		self.features.append('command')
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/waf-tools/pkgconfig.py	Fri Oct 23 16:23:38 2009 +0400
     5.3 @@ -0,0 +1,71 @@
     5.4 +# -*- mode: python; encoding: utf-8 -*-
     5.5 +# Gustavo Carneiro (gjamc) 2008
     5.6 +
     5.7 +import Options
     5.8 +import Configure
     5.9 +import pproc as subprocess
    5.10 +import config_c
    5.11 +
    5.12 +def detect(conf):
    5.13 +	pkg_config = conf.find_program('pkg-config', var='PKG_CONFIG')
    5.14 +	if not pkg_config: return
    5.15 +
    5.16 +@Configure.conf
    5.17 +def pkg_check_modules(conf, uselib_name, expression, mandatory=True):
    5.18 +	pkg_config = conf.env['PKG_CONFIG']
    5.19 +	if not pkg_config:
    5.20 +		if mandatory:
    5.21 +			conf.fatal("pkg-config is not available")
    5.22 +		else:
    5.23 +			return False
    5.24 +
    5.25 +	argv = [pkg_config, '--cflags', '--libs', expression]
    5.26 +	cmd = subprocess.Popen(argv, stdout=subprocess.PIPE)
    5.27 +	out, dummy = cmd.communicate()
    5.28 +	retval = cmd.wait()
    5.29 +
    5.30 +	msg_checking = ("pkg-config flags for %s" % (uselib_name,))
    5.31 +	if Options.options.verbose:
    5.32 +		if retval == 0:
    5.33 +			conf.check_message_custom(msg_checking,
    5.34 +						  ('(%s)' % expression), out)
    5.35 +		else:
    5.36 +			conf.check_message(msg_checking, ('(%s)' % expression), False)
    5.37 +	else:
    5.38 +		conf.check_message(msg_checking, '', (retval == 0), '')
    5.39 +	conf.log.write('%r: %r (exit code %i)\n' % (argv, out, retval))
    5.40 +
    5.41 +	if retval == 0:
    5.42 +
    5.43 +		config_c.parse_flags(out, uselib_name, conf.env)
    5.44 +		conf.env[uselib_name] = True
    5.45 +		return True
    5.46 +
    5.47 +	else:
    5.48 +
    5.49 +		conf.env[uselib_name] = False
    5.50 +		if mandatory:
    5.51 +			raise Configure.ConfigurationError('pkg-config check failed')
    5.52 +		else:
    5.53 +			return False
    5.54 +
    5.55 +@Configure.conf
    5.56 +def pkg_check_module_variable(conf, module, variable):
    5.57 +	pkg_config = conf.env['PKG_CONFIG']
    5.58 +	if not pkg_config:
    5.59 +		conf.fatal("pkg-config is not available")
    5.60 +
    5.61 +	argv = [pkg_config, '--variable', variable, module]
    5.62 +	cmd = subprocess.Popen(argv, stdout=subprocess.PIPE)
    5.63 +	out, dummy = cmd.communicate()
    5.64 +	retval = cmd.wait()
    5.65 +	out = out.rstrip() # strip the trailing newline
    5.66 +
    5.67 +	msg_checking = ("pkg-config variable %r in %s" % (variable, module,))
    5.68 +	conf.check_message_custom(msg_checking, '', out)
    5.69 +	conf.log.write('%r: %r (exit code %i)\n' % (argv, out, retval))
    5.70 +
    5.71 +	if retval == 0:
    5.72 +		return out
    5.73 +	else:
    5.74 +		raise Configure.ConfigurationError('pkg-config check failed')
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/waf-tools/shellcmd.py	Fri Oct 23 16:23:38 2009 +0400
     6.3 @@ -0,0 +1,345 @@
     6.4 +# Copyright (C) 2008 Gustavo J. A. M. Carneiro  <gjcarneiro@gmail.com>
     6.5 +
     6.6 +# This program is free software; you can redistribute it and/or modify
     6.7 +# it under the terms of the GNU General Public License as published by
     6.8 +# the Free Software Foundation; either version 2 of the License, or
     6.9 +# (at your option) any later version.
    6.10 +
    6.11 +# This program is distributed in the hope that it will be useful,
    6.12 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.13 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.14 +# GNU General Public License for more details.
    6.15 +
    6.16 +# You should have received a copy of the GNU General Public License
    6.17 +# along with this program; if not, write to the Free Software
    6.18 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    6.19 +
    6.20 +import shlex
    6.21 +import subprocess
    6.22 +import sys
    6.23 +import re
    6.24 +import os
    6.25 +
    6.26 +env_var_rx = re.compile(r"^([a-zA-Z0-9_]+)=(\S+)$")
    6.27 +
    6.28 +def debug(message):
    6.29 +    print >> sys.stderr, message
    6.30 +
    6.31 +
    6.32 +if sys.platform == 'win32':
    6.33 +    dev_null = open("NUL:", "w")
    6.34 +else:
    6.35 +    dev_null = open("/dev/null", "w")
    6.36 +
    6.37 +def _open_out_file(filename):
    6.38 +    if filename in ['NUL:', '/dev/null']:
    6.39 +        return dev_null
    6.40 +    else:
    6.41 +        return open(filename, 'wb')
    6.42 +
    6.43 +
    6.44 +class Node(object):
    6.45 +    pass
    6.46 +
    6.47 +class Op(Node):
    6.48 +    pass
    6.49 +
    6.50 +class Pipe(Op):
    6.51 +    pass
    6.52 +
    6.53 +class And(Op):
    6.54 +    pass
    6.55 +
    6.56 +class Or(Op):
    6.57 +    pass
    6.58 +
    6.59 +class Command(Node):
    6.60 +    class PIPE(object):
    6.61 +        pass # PIPE is a constant
    6.62 +    class STDOUT(object):
    6.63 +        pass # PIPE is a constant
    6.64 +
    6.65 +    def __init__(self, name):
    6.66 +        super(Command, self).__init__()
    6.67 +        self.name = name # command name
    6.68 +        self.argv = [name] # command argv
    6.69 +        self.stdin = None
    6.70 +        self.stdout = None
    6.71 +        self.stderr = None
    6.72 +        self.env_vars = None
    6.73 +
    6.74 +    def __repr__(self):
    6.75 +        return "Command(%r, argv=%r, stdin=%r, stdout=%r, stderr=%r)" \
    6.76 +            % (self.name, self.argv, self.stdin, self.stdout, self.stderr)
    6.77 +
    6.78 +class Chdir(Node):
    6.79 +    def __init__(self):
    6.80 +        super(Chdir, self).__init__()
    6.81 +        self.dir = None
    6.82 +
    6.83 +    def __repr__(self):
    6.84 +        return "Chdir(%r)" \
    6.85 +            % (self.dir)
    6.86 +
    6.87 +class Pipeline(object):
    6.88 +    def __init__(self):
    6.89 +        self.current_command = None
    6.90 +        self.pipeline = []
    6.91 +
    6.92 +    def _commit_command(self):
    6.93 +        assert self.current_command is not None
    6.94 +        self.pipeline.append(self.current_command)
    6.95 +        self.current_command = None
    6.96 +
    6.97 +    def get_abbreviated_command(self):
    6.98 +        l = []
    6.99 +        for node in self.pipeline:
   6.100 +            if isinstance(node, Command):
   6.101 +                l.append(node.name)
   6.102 +            if isinstance(node, Chdir):
   6.103 +                l.append('cd %s' % node.dir)
   6.104 +            elif isinstance(node, Pipe):
   6.105 +                l.append('|')
   6.106 +            elif isinstance(node, And):
   6.107 +                l.append('&&')
   6.108 +            elif isinstance(node, And):
   6.109 +                l.append('||')
   6.110 +        return ' '.join(l)
   6.111 +
   6.112 +    def parse(self, command):
   6.113 +        self.current_command = None
   6.114 +        self.pipeline = []
   6.115 +
   6.116 +        if isinstance(command, list):
   6.117 +            tokens = list(command)
   6.118 +        else:
   6.119 +            tokens = shlex.split(command)
   6.120 +        debug("command: shlex: %r" % (tokens,))
   6.121 +
   6.122 +        BEGIN, COMMAND, CHDIR, STDERR, STDOUT, STDIN = range(6)
   6.123 +        state = BEGIN
   6.124 +        self.current_command = None
   6.125 +        env_vars = dict()
   6.126 +
   6.127 +        while tokens:
   6.128 +            token = tokens.pop(0)
   6.129 +            if state == BEGIN:
   6.130 +                env_var_match = env_var_rx.match(token)
   6.131 +                if env_var_match is not None:
   6.132 +                    env_vars[env_var_match.group(1)] = env_var_match.group(2)
   6.133 +                else:
   6.134 +                    assert self.current_command is None
   6.135 +                    if token == 'cd':
   6.136 +                        self.current_command = Chdir()
   6.137 +                        assert not env_vars
   6.138 +                        state = CHDIR
   6.139 +                    else:
   6.140 +                        self.current_command = Command(token)
   6.141 +                        if env_vars:
   6.142 +                            self.current_command.env_vars = env_vars
   6.143 +                            env_vars = dict()
   6.144 +                        state = COMMAND
   6.145 +            elif state == COMMAND:
   6.146 +                if token == '>':
   6.147 +                    state = STDOUT
   6.148 +                elif token == '2>':
   6.149 +                    state = STDERR
   6.150 +                elif token == '2>&1':
   6.151 +                    assert self.current_command.stderr is None
   6.152 +                    self.current_command.stderr = Command.STDOUT
   6.153 +                elif token == '<':
   6.154 +                    state = STDIN
   6.155 +                elif token == '|':
   6.156 +                    assert self.current_command.stdout is None
   6.157 +                    self.current_command.stdout = Command.PIPE
   6.158 +                    self._commit_command()
   6.159 +                    self.pipeline.append(Pipe())
   6.160 +                    state = BEGIN
   6.161 +                elif token == '&&':
   6.162 +                    self._commit_command()
   6.163 +                    self.pipeline.append(And())
   6.164 +                    state = BEGIN
   6.165 +                elif token == '||':
   6.166 +                    self._commit_command()
   6.167 +                    self.pipeline.append(Or())
   6.168 +                    state = BEGIN
   6.169 +                else:
   6.170 +                    self.current_command.argv.append(token)
   6.171 +            elif state == CHDIR:
   6.172 +                if token == '&&':
   6.173 +                    self._commit_command()
   6.174 +                    self.pipeline.append(And())
   6.175 +                    state = BEGIN
   6.176 +                else:
   6.177 +                    assert self.current_command.dir is None
   6.178 +                    self.current_command.dir = token
   6.179 +            elif state == STDOUT:
   6.180 +                assert self.current_command.stdout is None
   6.181 +                self.current_command.stdout = token
   6.182 +                state = COMMAND
   6.183 +            elif state == STDERR:
   6.184 +                assert self.current_command.stderr is None
   6.185 +                self.current_command.stderr = token
   6.186 +                state = COMMAND
   6.187 +            elif state == STDIN:
   6.188 +                assert self.current_command.stdin is None
   6.189 +                self.current_command.stdin = token
   6.190 +                state = COMMAND
   6.191 +        self._commit_command()
   6.192 +        return self.pipeline
   6.193 +
   6.194 +    def _exec_piped_commands(self, commands):
   6.195 +        retvals = []
   6.196 +        for cmd in commands:
   6.197 +            retvals.append(cmd.wait())
   6.198 +        retval = 0
   6.199 +        for r in retvals:
   6.200 +            if r:
   6.201 +                retval = retvals[-1]
   6.202 +                break
   6.203 +        return retval
   6.204 +
   6.205 +    def run(self, verbose=False):
   6.206 +        pipeline = list(self.pipeline)
   6.207 +        files_to_close = []
   6.208 +        piped_commands = []
   6.209 +        piped_commands_display = []
   6.210 +        BEGIN, PIPE = range(2)
   6.211 +        state = BEGIN
   6.212 +        cwd = '.'
   6.213 +        while pipeline:
   6.214 +            node = pipeline.pop(0)
   6.215 +
   6.216 +            if isinstance(node, Chdir):
   6.217 +                next_op = pipeline.pop(0)
   6.218 +                assert isinstance(next_op, And)
   6.219 +                cwd = os.path.join(cwd, node.dir)
   6.220 +                if verbose:
   6.221 +                    piped_commands_display.append("cd %s &&" % node.dir)
   6.222 +                continue
   6.223 +            
   6.224 +            assert isinstance(node, (Command, Chdir))
   6.225 +            cmd = node
   6.226 +            if verbose:
   6.227 +                if cmd.env_vars:
   6.228 +                    env_vars_str = ' '.join(['%s=%s' % (key, val) for key, val in cmd.env_vars.iteritems()])
   6.229 +                    piped_commands_display.append("%s %s" % (env_vars_str, ' '.join(cmd.argv)))
   6.230 +                else:
   6.231 +                    piped_commands_display.append(' '.join(cmd.argv))
   6.232 +
   6.233 +            if state == PIPE:
   6.234 +                stdin = piped_commands[-1].stdout
   6.235 +            elif cmd.stdin is not None:
   6.236 +                stdin = open(cmd.stdin, "r")
   6.237 +                if verbose:
   6.238 +                    piped_commands_display.append('< %s' % cmd.stdin)
   6.239 +                files_to_close.append(stdin)
   6.240 +            else:
   6.241 +                stdin = None
   6.242 +
   6.243 +            if cmd.stdout is None:
   6.244 +                stdout = None
   6.245 +            elif cmd.stdout is Command.PIPE:
   6.246 +                stdout = subprocess.PIPE
   6.247 +            else:
   6.248 +                stdout = _open_out_file(cmd.stdout)
   6.249 +                files_to_close.append(stdout)
   6.250 +                if verbose:
   6.251 +                    piped_commands_display.append('> %s' % cmd.stdout)
   6.252 +
   6.253 +            if cmd.stderr is None:
   6.254 +                stderr = None
   6.255 +            elif cmd.stderr is Command.PIPE:
   6.256 +                stderr = subprocess.PIPE
   6.257 +            elif cmd.stderr is Command.STDOUT:
   6.258 +                stderr = subprocess.STDOUT
   6.259 +                if verbose:
   6.260 +                    piped_commands_display.append('2>&1')
   6.261 +            else:
   6.262 +                stderr = _open_out_file(cmd.stderr)
   6.263 +                files_to_close.append(stderr)
   6.264 +                if verbose:
   6.265 +                    piped_commands_display.append('2> %s' % cmd.stderr)
   6.266 +
   6.267 +            if cmd.env_vars:
   6.268 +                env = dict(os.environ)
   6.269 +                env.update(cmd.env_vars)
   6.270 +            else:
   6.271 +                env = None
   6.272 +
   6.273 +            if cwd == '.':
   6.274 +                proc_cwd = None
   6.275 +            else:
   6.276 +                proc_cwd = cwd
   6.277 +
   6.278 +            debug("command: subprocess.Popen(argv=%r, stdin=%r, stdout=%r, stderr=%r, env_vars=%r, cwd=%r)"
   6.279 +                  % (cmd.argv, stdin, stdout, stderr, cmd.env_vars, proc_cwd))
   6.280 +            proc = subprocess.Popen(cmd.argv, stdin=stdin, stdout=stdout, stderr=stderr, env=env, cwd=proc_cwd)
   6.281 +            del stdin, stdout, stderr
   6.282 +            piped_commands.append(proc)
   6.283 +
   6.284 +            try:
   6.285 +                next_node = pipeline.pop(0)
   6.286 +            except IndexError:
   6.287 +                try:
   6.288 +                    retval = self._exec_piped_commands(piped_commands)
   6.289 +                    if verbose:
   6.290 +                        print "%s: exit code %i" % (' '.join(piped_commands_display), retval)
   6.291 +                finally:
   6.292 +                    for f in files_to_close:
   6.293 +                        if f is not dev_null:
   6.294 +                            f.close()
   6.295 +                    files_to_close = []
   6.296 +                return retval
   6.297 +            else:
   6.298 +
   6.299 +                if isinstance(next_node, Pipe):
   6.300 +                    state = PIPE
   6.301 +                    piped_commands_display.append('|')
   6.302 +
   6.303 +                elif isinstance(next_node, Or):
   6.304 +                    try:
   6.305 +                        this_retval = self._exec_piped_commands(piped_commands)
   6.306 +                    finally:
   6.307 +                        for f in files_to_close:
   6.308 +                            if f is not dev_null:
   6.309 +                                f.close()
   6.310 +                        files_to_close = []
   6.311 +                    if this_retval == 0:
   6.312 +                        if verbose:
   6.313 +                            print "%s: exit code %i (|| is short-circuited)" % (' '.join(piped_commands_display), retval)
   6.314 +                        return this_retval
   6.315 +                    if verbose:
   6.316 +                        print "%s: exit code %i (|| proceeds)" % (' '.join(piped_commands_display), retval)
   6.317 +                    state = BEGIN
   6.318 +                    piped_commands = []
   6.319 +                    piped_commands_display = []
   6.320 +
   6.321 +                elif isinstance(next_node, And):
   6.322 +                    try:
   6.323 +                        this_retval = self._exec_piped_commands(piped_commands)
   6.324 +                    finally:
   6.325 +                        for f in files_to_close:
   6.326 +                            if f is not dev_null:
   6.327 +                                f.close()
   6.328 +                        files_to_close = []
   6.329 +                    if this_retval != 0:
   6.330 +                        if verbose:
   6.331 +                            print "%s: exit code %i (&& is short-circuited)" % (' '.join(piped_commands_display), retval)
   6.332 +                        return this_retval
   6.333 +                    if verbose:
   6.334 +                        print "%s: exit code %i (&& proceeds)" % (' '.join(piped_commands_display), retval)
   6.335 +                    state = BEGIN
   6.336 +                    piped_commands = []
   6.337 +                    piped_commands_display = []
   6.338 +
   6.339 +
   6.340 +
   6.341 +def _main():
   6.342 +    pipeline = Pipeline()
   6.343 +    pipeline.parse('./foo.py 2>&1 < xxx | cat && ls')
   6.344 +    print pipeline.run()
   6.345 +
   6.346 +if __name__ == '__main__':
   6.347 +    _main()
   6.348 +
     7.1 --- a/wscript	Fri Oct 23 16:22:56 2009 +0400
     7.2 +++ b/wscript	Fri Oct 23 16:23:38 2009 +0400
     7.3 @@ -27,6 +27,8 @@
     7.4  import Configure
     7.5  import Scripting
     7.6  
     7.7 +sys.path.insert(0, os.path.abspath('waf-tools'))
     7.8 +
     7.9  import cflags # override the build profiles from waf
    7.10  cflags.profiles = {
    7.11  	# profile name: [optimization_level, warnings_level, debug_level]