aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/backend/ninjabackend.py
diff options
context:
space:
mode:
authorJon Turney <jon.turney@dronecode.org.uk>2019-04-13 17:07:19 +0100
committerDan Kegel <dank@kegel.com>2020-06-05 14:15:32 -0700
commitfbacf87af525f7b69e4f1a310f6ef38c5853406c (patch)
treedf1fd14ec7ee129283ce25420f08552a5476c486 /mesonbuild/backend/ninjabackend.py
parent9cec5f3521407d50307eeef3c34cbd608949b0a1 (diff)
downloadmeson-fbacf87af525f7b69e4f1a310f6ef38c5853406c.zip
meson-fbacf87af525f7b69e4f1a310f6ef38c5853406c.tar.gz
meson-fbacf87af525f7b69e4f1a310f6ef38c5853406c.tar.bz2
ninja: Quoting in rspfile depends on the compiler, not the shell
In certain exotic configurations, the style of quoting expected in the response file may not match that expected by the shell. e.g. under MSYS2, ninja invokes commands via CreateProcess (which results in cmd-style quoting processed by parse_cmdline or CommandLineToArgvW), but gcc will use sh-style quoting in any response file it reads. Future work: The rspfile quoting style should be a method of the compiler or linker object, rather than hardcoded in ninjabackend. (In fact, can_linker_accept_rsp() should be extended to do this, since if we can accept rsp, we should know the quoting style)
Diffstat (limited to 'mesonbuild/backend/ninjabackend.py')
-rw-r--r--mesonbuild/backend/ninjabackend.py65
1 files changed, 48 insertions, 17 deletions
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 9af079e..5743d48 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -15,6 +15,7 @@ import typing as T
import os
import re
import pickle
+import shlex
import subprocess
from collections import OrderedDict
from enum import Enum, unique
@@ -29,9 +30,14 @@ from .. import build
from .. import mlog
from .. import dependencies
from .. import compilers
-from ..compilers import (Compiler, CompilerArgs, CCompiler, FortranCompiler,
- PGICCompiler, VisualStudioLikeCompiler)
-from ..linkers import ArLinker
+from ..compilers import (
+ Compiler, CompilerArgs, CCompiler,
+ DmdDCompiler,
+ FortranCompiler, PGICCompiler,
+ VisualStudioCsCompiler,
+ VisualStudioLikeCompiler,
+)
+from ..linkers import ArLinker, VisualStudioLinker
from ..mesonlib import (
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine,
ProgressBar, quote_arg, unholder,
@@ -46,12 +52,15 @@ FORTRAN_MODULE_PAT = r"^\s*\bmodule\b\s+(\w+)\s*(?:!+.*)*$"
FORTRAN_SUBMOD_PAT = r"^\s*\bsubmodule\b\s*\((\w+:?\w+)\)\s*(\w+)"
FORTRAN_USE_PAT = r"^\s*use,?\s*(?:non_intrinsic)?\s*(?:::)?\s*(\w+)"
+def cmd_quote(s):
+ # XXX: this needs to understand how to escape any existing double quotes(")
+ return '"{}"'.format(s)
+
+# How ninja executes command lines differs between Unix and Windows
+# (see https://ninja-build.org/manual.html#ref_rule_command)
if mesonlib.is_windows():
- # FIXME: can't use quote_arg on Windows just yet; there are a number of existing workarounds
- # throughout the codebase that cumulatively make the current code work (see, e.g. Backend.escape_extra_args
- # and NinjaBuildElement.write below) and need to be properly untangled before attempting this
- quote_func = lambda s: '"{}"'.format(s)
- execute_wrapper = ['cmd', '/c']
+ quote_func = cmd_quote
+ execute_wrapper = ['cmd', '/c'] # unused
rmfile_prefix = ['del', '/f', '/s', '/q', '{}', '&&']
else:
quote_func = quote_arg
@@ -115,7 +124,8 @@ class NinjaComment:
class NinjaRule:
def __init__(self, rule, command, args, description,
- rspable = False, deps = None, depfile = None, extra = None):
+ rspable = False, deps = None, depfile = None, extra = None,
+ rspfile_quote_style = 'sh'):
def strToCommandArg(c):
if isinstance(c, NinjaCommandArg):
@@ -149,20 +159,26 @@ class NinjaRule:
self.rspable = rspable # if a rspfile can be used
self.refcount = 0
self.rsprefcount = 0
+ self.rspfile_quote_style = rspfile_quote_style # rspfile quoting style is 'sh' or 'cl'
@staticmethod
- def _quoter(x):
+ def _quoter(x, qf = quote_func):
if isinstance(x, NinjaCommandArg):
if x.quoting == Quoting.none:
return x.s
elif x.quoting == Quoting.notNinja:
- return quote_func(x.s)
+ return qf(x.s)
elif x.quoting == Quoting.notShell:
return ninja_quote(x.s)
# fallthrough
- return ninja_quote(quote_func(str(x)))
+ return ninja_quote(qf(str(x)))
def write(self, outfile):
+ if self.rspfile_quote_style == 'cl':
+ rspfile_quote_func = cmd_quote
+ else:
+ rspfile_quote_func = shlex.quote
+
def rule_iter():
if self.refcount:
yield ''
@@ -174,7 +190,7 @@ class NinjaRule:
if rsp == '_RSP':
outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command])))
outfile.write(' rspfile = $out.rsp\n')
- outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x) for x in self.args])))
+ outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x, rspfile_quote_func) for x in self.args])))
else:
outfile.write(' command = {}\n'.format(' '.join([self._quoter(x) for x in (self.command + self.args)])))
if self.deps:
@@ -285,7 +301,8 @@ class NinjaBuildElement:
implicit_outs = ' '.join([ninja_quote(i, True) for i in self.implicit_outfilenames])
if implicit_outs:
implicit_outs = ' | ' + implicit_outs
- if self._should_use_rspfile():
+ use_rspfile = self._should_use_rspfile()
+ if use_rspfile:
rulename = self.rulename + '_RSP'
mlog.log("Command line for building %s is long, using a response file" % self.outfilenames)
else:
@@ -304,6 +321,14 @@ class NinjaBuildElement:
line = line.replace('\\', '/')
outfile.write(line)
+ if use_rspfile:
+ if self.rule.rspfile_quote_style == 'cl':
+ qf = cmd_quote
+ else:
+ qf = shlex.quote
+ else:
+ qf = quote_func
+
for e in self.elems:
(name, elems) = e
should_quote = name not in raw_names
@@ -313,9 +338,9 @@ class NinjaBuildElement:
if not should_quote or i == '&&': # Hackety hack hack
quoter = ninja_quote
else:
- quoter = lambda x: ninja_quote(quote_func(x))
+ quoter = lambda x: ninja_quote(qf(x))
i = i.replace('\\', '\\\\')
- if quote_func('') == '""':
+ if qf('') == '""':
i = i.replace('"', '\\"')
newelems.append(quoter(i))
line += ' '.join(newelems)
@@ -1694,6 +1719,7 @@ int dummy;
pool = None
self.add_rule(NinjaRule(rule, cmdlist, args, description,
rspable=static_linker.can_linker_accept_rsp(),
+ rspfile_quote_style='cl' if isinstance(static_linker, VisualStudioLinker) else 'sh',
extra=pool))
def generate_dynamic_link_rules(self):
@@ -1716,6 +1742,8 @@ int dummy;
pool = None
self.add_rule(NinjaRule(rule, command, args, description,
rspable=compiler.can_linker_accept_rsp(),
+ rspfile_quote_style='cl' if (compiler.get_argument_syntax() == 'msvc' or
+ isinstance(compiler, DmdDCompiler)) else 'sh',
extra=pool))
args = self.environment.get_build_command() + \
@@ -1743,7 +1771,8 @@ int dummy;
args = ['$ARGS', '$in']
description = 'Compiling C Sharp target $out'
self.add_rule(NinjaRule(rule, command, args, description,
- rspable=mesonlib.is_windows()))
+ rspable=mesonlib.is_windows(),
+ rspfile_quote_style='cl' if isinstance(compiler, VisualStudioCsCompiler) else 'sh'))
def generate_vala_compile_rules(self, compiler):
rule = self.compiler_to_rule_name(compiler)
@@ -1829,6 +1858,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
depfile = '$DEPFILE'
self.add_rule(NinjaRule(rule, command, args, description,
rspable=compiler.can_linker_accept_rsp(),
+ rspfile_quote_style='cl' if (compiler.get_argument_syntax() == 'msvc' or
+ isinstance(compiler, DmdDCompiler)) else 'sh',
deps=deps, depfile=depfile))
def generate_pch_rule_for(self, langname, compiler):