Simplify long command processing

This commit is contained in:
Thomas Nagy 2016-06-15 20:24:34 +02:00
parent 4a09e1943a
commit 537d1fcd45
No known key found for this signature in database
GPG Key ID: 67A565EDFDF90E64
5 changed files with 61 additions and 162 deletions

View File

@ -6,7 +6,7 @@
Tasks represent atomic operations such as processes.
"""
import re, sys
import os, re, sys, tempfile
from waflib import Utils, Logs, Errors
# task states
@ -174,6 +174,19 @@ class TaskBase(evil):
self.generator.bld.fatal('Working folders given to tasks must be Node objects')
return ret
def quote_flag(self, x):
old = x
if '\\' in x:
x = x.replace('\\', '\\\\')
if '"' in x:
x = x.replace('"', '\\"')
if old != x or ' ' in x or '\t' in x or "'" in x:
x = '"%s"' % x
return x
def split_argfile(self, cmd):
return ([cmd[0]], [self.quote_flag(x) for x in cmd[1:]])
def exec_command(self, cmd, **kw):
"""
Wrapper for :py:meth:`waflib.Context.Context.exec_command` which sets a current working directory to ``build.variant_dir``
@ -183,7 +196,24 @@ class TaskBase(evil):
"""
if not 'cwd' in kw:
kw['cwd'] = self.get_cwd()
return self.generator.bld.exec_command(cmd, **kw)
# workaround for command line length limit:
# http://support.microsoft.com/kb/830473
if not isinstance(cmd, str) and len(repr(cmd)) >= 8192:
cmd, args = self.split_argfile(cmd)
try:
(fd, tmp) = tempfile.mkstemp()
os.write(fd, '\r\n'.join(args).encode())
os.close(fd)
return self.generator.bld.exec_command(cmd + ['@' + tmp], **kw)
finally:
try:
os.remove(tmp)
except OSError:
# anti-virus and indexers can keep files open -_-
pass
else:
return self.generator.bld.exec_command(cmd, **kw)
def runnable_status(self):
"""

View File

@ -126,41 +126,9 @@ class mcs(Task.Task):
run_str = '${MCS} ${CSTYPE} ${CSFLAGS} ${ASS_ST:ASSEMBLIES} ${RES_ST:RESOURCES} ${OUT} ${SRC}'
def exec_command(self, cmd, **kw):
if not 'cwd' in kw:
kw['cwd'] = self.get_cwd()
try:
tmp = None
if isinstance(cmd, list) and len(' '.join(cmd)) >= 8192:
program = cmd[0] #unquoted program name, otherwise exec_command will fail
cmd = [self.quote_response_command(x) for x in cmd]
(fd, tmp) = tempfile.mkstemp()
os.write(fd, '\r\n'.join(i.replace('\\', '\\\\') for i in cmd[1:]).encode())
os.close(fd)
cmd = [program, '@' + tmp]
# no return here, that's on purpose
ret = self.generator.bld.exec_command(cmd, **kw)
finally:
if tmp:
try:
os.remove(tmp)
except OSError:
pass # anti-virus and indexers can keep the files open -_-
return ret
def quote_response_command(self, flag):
# /noconfig is not allowed when using response files
if flag.lower() == '/noconfig':
return ''
if flag.find(' ') > -1:
for x in ('/r:', '/reference:', '/resource:', '/lib:', '/out:'):
if flag.startswith(x):
flag = '%s"%s"' % (x, '","'.join(flag[len(x):].split(',')))
break
else:
flag = '"%s"' % flag
return flag
if '/noconfig' in cmd:
raise ValueError('/noconfig is not allowed when using response files, check your flags!')
return super(self.__class__, self).exec_command(cmd, **kw)
def configure(conf):
"""

View File

@ -94,8 +94,8 @@ def configure(conf):
conf.fc_add_flags()
conf.ifort_modifier_platform()
import os, sys, re, tempfile
from waflib import Task, Logs, Options, Errors
import os, re
from waflib import Task, Logs, Errors
from waflib.TaskGen import after_method, feature
from waflib.Configure import conf
@ -449,38 +449,7 @@ def exec_mf(self):
lst.extend(['-manifest', manifest])
lst.append('-outputresource:%s;%s' % (outfile, mode))
return self.exec_command(lst)
def quote_response_command(self, flag):
if flag.find(' ') > -1:
for x in ('/LIBPATH:', '/IMPLIB:', '/OUT:', '/I'):
if flag.startswith(x):
flag = '%s"%s"' % (x, flag[len(x):])
break
else:
flag = '"%s"' % flag
return flag
def exec_response_command(self, cmd, **kw):
# not public yet
try:
tmp = None
if sys.platform.startswith('win') and isinstance(cmd, list) and len(' '.join(cmd)) >= 8192:
program = cmd[0] #unquoted program name, otherwise exec_command will fail
cmd = [self.quote_response_command(x) for x in cmd]
(fd, tmp) = tempfile.mkstemp()
os.write(fd, '\r\n'.join(i.replace('\\', '\\\\') for i in cmd[1:]).encode())
os.close(fd)
cmd = [program, '@' + tmp]
# no return here, that's on purpose
ret = super(self.__class__, self).exec_command(cmd, **kw)
finally:
if tmp:
try:
os.remove(tmp)
except OSError:
pass # anti-virus and indexers can keep the files open -_-
return ret
return super(self.__class__, self).exec_command(lst)
def exec_command_ifort(self, *k, **kw):
"""
@ -503,10 +472,9 @@ def exec_command_ifort(self, *k, **kw):
env.update(PATH = ';'.join(self.env['PATH']))
kw['env'] = env
if not 'cwd' in kw:
kw['cwd'] = self.get_cwd()
ret = self.exec_response_command(k[0], **kw)
ret = super(self.__class__, self).exec_command(k[0], **kw)
if not ret and getattr(self, 'do_manifest', None):
ret = self.exec_mf()
return ret
@ -527,15 +495,13 @@ def wrap_class(class_name):
if self.env.IFORT_WIN32:
return self.exec_command_ifort(*k, **kw)
else:
return super(derived_class, self).exec_command(*k, **kw)
return super(self.__class__, self).exec_command(*k, **kw)
# Chain-up monkeypatch needed since exec_command() is in base class API
derived_class.exec_command = exec_command
# No chain-up behavior needed since the following methods aren't in
# base class API
derived_class.exec_response_command = exec_response_command
derived_class.quote_response_command = quote_response_command
derived_class.exec_command_ifort = exec_command_ifort
derived_class.exec_mf = exec_mf

View File

@ -26,8 +26,8 @@ You would have to run::
[1] http://www.jython.org/
"""
import os, tempfile, shutil
from waflib import Task, Utils, Errors, Node, Logs
import os, shutil
from waflib import Task, Utils, Errors, Node
from waflib.Configure import conf
from waflib.TaskGen import feature, before_method, after_method
@ -210,7 +210,19 @@ def use_jar_files(self):
y.post()
self.jar_task.run_after.update(y.tasks)
class jar_create(Task.Task):
class JTask(Task.Task):
def split_argfile(self, cmd):
inline = [cmd[0]]
infile = []
for x in cmd[1:]:
# jar and javac do not want -J flags in @file
if x.startswith('-J'):
inline.append(x)
else:
infile.append(self.quote_flag(x))
return (inline, infile)
class jar_create(JTask):
"""
Create a jar file
"""
@ -233,12 +245,12 @@ class jar_create(Task.Task):
raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self))
return super(jar_create, self).runnable_status()
class javac(Task.Task):
class javac(JTask):
"""
Compile java files
"""
color = 'BLUE'
run_str = '${JAVAC} -classpath ${CLASSPATH} -d ${OUTDIR} ${JAVACFLAGS} ${SRC}'
vars = ['CLASSPATH', 'JAVACFLAGS', 'JAVAC', 'OUTDIR']
"""
The javac task will be executed again if the variables CLASSPATH, JAVACFLAGS, JAVAC or OUTDIR change.
@ -265,50 +277,6 @@ class javac(Task.Task):
self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False))
return super(javac, self).runnable_status()
def run(self):
"""
Execute the javac compiler
"""
env = self.env
gen = self.generator
bld = gen.bld
wd = bld.bldnode
def to_list(xx):
if isinstance(xx, str): return [xx]
return xx
cmd = []
cmd.extend(to_list(env['JAVAC']))
cmd.extend(['-classpath'])
cmd.extend(to_list(env['CLASSPATH']))
cmd.extend(['-d'])
cmd.extend(to_list(env['OUTDIR']))
cmd.extend(to_list(env['JAVACFLAGS']))
files = [a.path_from(self.get_cwd()) for a in self.inputs]
# workaround for command line length limit:
# http://support.microsoft.com/kb/830473
tmp = None
try:
if len(str(files)) + len(str(cmd)) > 8192:
(fd, tmp) = tempfile.mkstemp(dir=bld.bldnode.abspath())
try:
os.write(fd, '\n'.join(files).encode())
finally:
if tmp:
os.close(fd)
if Logs.verbose:
Logs.debug('runner: %r', cmd + files)
cmd.append('@' + tmp)
else:
cmd += files
ret = self.exec_command(cmd, cwd=wd, env=env.env or None)
finally:
if tmp:
os.remove(tmp)
return ret
def post_run(self):
"""
"""

View File

@ -52,7 +52,7 @@ cmd.exe /C "chcp 1252 & set PYTHONUNBUFFERED=true && set && waf configure"
Setting PYTHONUNBUFFERED gives the unbuffered output.
"""
import os, sys, re, tempfile
import os, sys, re
from waflib import Utils, Task, Logs, Options, Errors
from waflib.TaskGen import after_method, feature
@ -980,38 +980,7 @@ def exec_mf(self):
lst.extend(['-manifest', manifest])
lst.append('-outputresource:%s;%s' % (outfile, mode))
return self.exec_command(lst)
def quote_response_command(self, flag):
if flag.find(' ') > -1:
for x in ('/LIBPATH:', '/IMPLIB:', '/OUT:', '/I'):
if flag.startswith(x):
flag = '%s"%s"' % (x, flag[len(x):])
break
else:
flag = '"%s"' % flag
return flag
def exec_response_command(self, cmd, **kw):
# not public yet
try:
tmp = None
if sys.platform.startswith('win') and isinstance(cmd, list) and len(' '.join(cmd)) >= 8192:
program = cmd[0] #unquoted program name, otherwise exec_command will fail
cmd = [self.quote_response_command(x) for x in cmd]
(fd, tmp) = tempfile.mkstemp()
os.write(fd, '\r\n'.join(i.replace('\\', '\\\\') for i in cmd[1:]).encode())
os.close(fd)
cmd = [program, '@' + tmp]
# no return here, that's on purpose
ret = self.generator.bld.exec_command(cmd, **kw)
finally:
if tmp:
try:
os.remove(tmp)
except OSError:
pass # anti-virus and indexers can keep the files open -_-
return ret
return super(self.__class__, self).exec_command(lst)
########## stupid evil command modification: concatenate the tokens /Fx, /doc, and /x: with the next token
@ -1039,7 +1008,7 @@ def exec_command_msvc(self, *k, **kw):
if not 'cwd' in kw:
kw['cwd'] = self.get_cwd()
ret = self.exec_response_command(k[0], **kw)
ret = super(self.__class__, self).exec_command(k[0], **kw)
if not ret and getattr(self, 'do_manifest', None):
ret = self.exec_mf()
return ret
@ -1060,15 +1029,13 @@ def wrap_class(class_name):
if self.env['CC_NAME'] == 'msvc':
return self.exec_command_msvc(*k, **kw)
else:
return super(derived_class, self).exec_command(*k, **kw)
return super(self.__class__, self).exec_command(*k, **kw)
# Chain-up monkeypatch needed since exec_command() is in base class API
derived_class.exec_command = exec_command
# No chain-up behavior needed since the following methods aren't in
# base class API
derived_class.exec_response_command = exec_response_command
derived_class.quote_response_command = quote_response_command
derived_class.exec_command_msvc = exec_command_msvc
derived_class.exec_mf = exec_mf