From 5d4074eb7f0d0bd7890a372d69bf1d43152a052a Mon Sep 17 00:00:00 2001 From: Thomas Nagy Date: Sat, 4 Mar 2017 08:20:27 +0100 Subject: [PATCH] Display commands as string with "WAF_CMD_FORMAT=string waf build -v" --- ChangeLog | 1 + tests/utils/wscript | 39 ++++++++++++++++ waflib/Context.py | 14 ++++-- waflib/Utils.py | 20 +++++++- waflib/extras/print_commands.py | 82 --------------------------------- 5 files changed, 69 insertions(+), 87 deletions(-) create mode 100644 tests/utils/wscript delete mode 100644 waflib/extras/print_commands.py diff --git a/ChangeLog b/ChangeLog index 62324d29..62e904cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ NEW IN WAF 2.0.0 * Force new files into the build directory by default (use Node objects to bypass) * Process support for building over UNC paths * Simplify the Task class hierarchy; TaskBase is removed +* Display commands as string with "WAF_CMD_FORMAT=string waf build -v" * New ant_glob(..., generator=True) now returns a Python generator * Accept nested lists and generators in bld(source=...) * Sort TaskGen methods in alphabetical order by reversing TaskGen.prec order diff --git a/tests/utils/wscript b/tests/utils/wscript new file mode 100644 index 00000000..0d4ee976 --- /dev/null +++ b/tests/utils/wscript @@ -0,0 +1,39 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2012 (ita) + +VERSION='0.0.1' +APPNAME='preproc_test' +top = '.' +out = 'build' + +from waflib import Utils +from waflib.Logs import pprint + +def configure(conf): + pass + +def build(bld): + + bld.failure = 0 + def disp(color, result): + pprint(color, result) + if color == 'RED': + bld.failure=1 + def stop_status(bld): + if bld.failure: + bld.fatal('One or several test failed, check the outputs above') + bld.add_post_fun(stop_status) + + def test_shell(inp, expected): + ret = Utils.shell_escape(inp) + if ret == expected: + color = "GREEN" + else: + color = "RED" + disp(color, "%r -> %r\t\texpected: %r" % (inp, ret, expected)) + + test_shell("ls -l", "ls -l") + test_shell(['ls', '-l', 'a space'], "ls -l 'a space'") + + diff --git a/waflib/Context.py b/waflib/Context.py index a3718950..0366bd1f 100644 --- a/waflib/Context.py +++ b/waflib/Context.py @@ -296,6 +296,15 @@ class Context(ctx): raise Errors.WafError('Cannot read the folder %r' % d) raise Errors.WafError('No wscript file in directory %s' % d) + def log_command(self, cmd, kw): + if Logs.verbose: + fmt = os.environ.get('WAF_CMD_FORMAT') + if fmt == 'string': + if not isinstance(cmd, str): + cmd = Utils.shell_escape(cmd) + Logs.debug('runner: %r', cmd) + Logs.debug('runner_env: kw=%s', kw) + def exec_command(self, cmd, **kw): """ Runs an external process and returns the exit status:: @@ -317,8 +326,7 @@ class Context(ctx): """ subprocess = Utils.subprocess kw['shell'] = isinstance(cmd, str) - Logs.debug('runner: %r', cmd) - Logs.debug('runner_env: kw=%s', kw) + self.log_command(cmd, kw) if self.logger: self.logger.info(cmd) @@ -396,7 +404,7 @@ class Context(ctx): """ subprocess = Utils.subprocess kw['shell'] = isinstance(cmd, str) - Logs.debug('runner: %r', cmd) + self.log_command(cmd, kw) if 'quiet' in kw: quiet = kw['quiet'] diff --git a/waflib/Utils.py b/waflib/Utils.py index 62fae456..b864f311 100644 --- a/waflib/Utils.py +++ b/waflib/Utils.py @@ -568,10 +568,26 @@ def quote_define_name(s): fu = fu.upper() return fu +re_sh = re.compile('\\s|\'|"') +""" +Regexp used for shell_escape below +""" + +def shell_escape(cmd): + """ + Escapes a command: + ['ls', '-l', 'arg space'] -> ls -l 'arg space' + """ + if isinstance(cmd, str): + return cmd + return ' '.join(repr(x) if re_sh.search(x) else x for x in cmd) + def h_list(lst): """ - Hash lists. We would prefer to use hash(tup) for tuples because it is much more efficient, - but Python now enforces hash randomization by assuming everybody is running a web application. + Hashes lists of ordered data. + + Using hash(tup) for tuples would be much more efficient, + but Python now enforces hash randomization :param lst: list to hash :type lst: list of strings diff --git a/waflib/extras/print_commands.py b/waflib/extras/print_commands.py deleted file mode 100644 index 42233259..00000000 --- a/waflib/extras/print_commands.py +++ /dev/null @@ -1,82 +0,0 @@ -#! /usr/bin/env python - -""" -Illustrate how to override a class method to do something - -In this case, print the commands being executed as strings -(the commands are usually lists, so this can be misleading) -""" - -import sys -from waflib import Context, Utils, Errors, Logs - -def exec_command(self, cmd, **kw): - subprocess = Utils.subprocess - kw['shell'] = isinstance(cmd, str) - - if isinstance(cmd, str): - kw['shell'] = True - txt = cmd - else: - txt = ' '.join(repr(x) if ' ' in x else x for x in cmd) - - Logs.debug('runner: %s', txt) - Logs.debug('runner_env: kw=%s', kw) - - if self.logger: - self.logger.info(cmd) - - if 'stdout' not in kw: - kw['stdout'] = subprocess.PIPE - if 'stderr' not in kw: - kw['stderr'] = subprocess.PIPE - - if Logs.verbose and not kw['shell'] and not Utils.check_exe(cmd[0]): - raise Errors.WafError("Program %s not found!" % cmd[0]) - - wargs = {} - if 'timeout' in kw: - if kw['timeout'] is not None: - wargs['timeout'] = kw['timeout'] - del kw['timeout'] - if 'input' in kw: - if kw['input']: - wargs['input'] = kw['input'] - kw['stdin'] = Utils.subprocess.PIPE - del kw['input'] - - if 'cwd' in kw: - if not isinstance(kw['cwd'], str): - kw['cwd'] = kw['cwd'].abspath() - - try: - if kw['stdout'] or kw['stderr']: - p = subprocess.Popen(cmd, **kw) - (out, err) = p.communicate(**wargs) - ret = p.returncode - else: - out, err = (None, None) - ret = subprocess.Popen(cmd, **kw).wait(**wargs) - except Exception as e: - raise Errors.WafError('Execution failure: %s' % str(e), ex=e) - - if out: - if not isinstance(out, str): - out = out.decode(sys.stdout.encoding or 'latin-1') - if self.logger: - self.logger.debug('out: %s' % out) - else: - Logs.info(out, extra={'stream':sys.stdout, 'c1': ''}) - if err: - if not isinstance(err, str): - err = err.decode(sys.stdout.encoding or 'latin-1') - if self.logger: - self.logger.error('err: %s' % err) - else: - Logs.info(err, extra={'stream':sys.stderr, 'c1': ''}) - - return ret - -Context.Context.exec_command = exec_command - -