diff --git a/waflib/Task.py b/waflib/Task.py index 67909700..79dc7f04 100644 --- a/waflib/Task.py +++ b/waflib/Task.py @@ -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): """ diff --git a/waflib/Tools/cs.py b/waflib/Tools/cs.py index 02548374..eed0f11c 100644 --- a/waflib/Tools/cs.py +++ b/waflib/Tools/cs.py @@ -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): """ diff --git a/waflib/Tools/ifort.py b/waflib/Tools/ifort.py index ce1e9bf6..2d277c6b 100644 --- a/waflib/Tools/ifort.py +++ b/waflib/Tools/ifort.py @@ -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 diff --git a/waflib/Tools/javaw.py b/waflib/Tools/javaw.py index 72e16fe2..d8dcd93c 100644 --- a/waflib/Tools/javaw.py +++ b/waflib/Tools/javaw.py @@ -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): """ """ diff --git a/waflib/Tools/msvc.py b/waflib/Tools/msvc.py index d1a645eb..0c6b5638 100644 --- a/waflib/Tools/msvc.py +++ b/waflib/Tools/msvc.py @@ -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