ninja: Use shlex.quote for quoting on non-Windows
This is more reliable, and more accurate. For instance, this means arguments in commands aren't surrounded by `'` on Linux unless that is actually needed by that specific argument. There is no equivalent helper for Windows, so we keep the old behaviour for that.
This commit is contained in:
parent
ae9b23832e
commit
4b428053f4
|
@ -12,6 +12,11 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import shlex
|
||||||
|
import os, sys, pickle, re
|
||||||
|
import subprocess, shutil
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from . import backends
|
from . import backends
|
||||||
from .. import modules
|
from .. import modules
|
||||||
from .. import environment, mesonlib
|
from .. import environment, mesonlib
|
||||||
|
@ -24,16 +29,13 @@ from ..mesonlib import File, MesonException, OrderedSet
|
||||||
from ..mesonlib import get_meson_script, get_compiler_for_source
|
from ..mesonlib import get_meson_script, get_compiler_for_source
|
||||||
from .backends import CleanTrees, InstallData
|
from .backends import CleanTrees, InstallData
|
||||||
from ..build import InvalidArguments
|
from ..build import InvalidArguments
|
||||||
import os, sys, pickle, re
|
|
||||||
import subprocess, shutil
|
|
||||||
from collections import OrderedDict
|
|
||||||
|
|
||||||
if mesonlib.is_windows():
|
if mesonlib.is_windows():
|
||||||
quote_char = '"'
|
quote_func = lambda s: '"{}"'.format(s)
|
||||||
execute_wrapper = 'cmd /c'
|
execute_wrapper = 'cmd /c'
|
||||||
rmfile_prefix = 'del /f /s /q {} &&'
|
rmfile_prefix = 'del /f /s /q {} &&'
|
||||||
else:
|
else:
|
||||||
quote_char = "'"
|
quote_func = shlex.quote
|
||||||
execute_wrapper = ''
|
execute_wrapper = ''
|
||||||
rmfile_prefix = 'rm -f {} &&'
|
rmfile_prefix = 'rm -f {} &&'
|
||||||
|
|
||||||
|
@ -105,18 +107,17 @@ class NinjaBuildElement:
|
||||||
(name, elems) = e
|
(name, elems) = e
|
||||||
should_quote = name not in raw_names
|
should_quote = name not in raw_names
|
||||||
line = ' %s = ' % name
|
line = ' %s = ' % name
|
||||||
q_templ = quote_char + "%s" + quote_char
|
|
||||||
noq_templ = "%s"
|
noq_templ = "%s"
|
||||||
newelems = []
|
newelems = []
|
||||||
for i in elems:
|
for i in elems:
|
||||||
if not should_quote or i == '&&': # Hackety hack hack
|
if not should_quote or i == '&&': # Hackety hack hack
|
||||||
templ = noq_templ
|
quoter = ninja_quote
|
||||||
else:
|
else:
|
||||||
templ = q_templ
|
quoter = lambda x: ninja_quote(quote_func(x))
|
||||||
i = i.replace('\\', '\\\\')
|
i = i.replace('\\', '\\\\')
|
||||||
if quote_char == '"':
|
if quote_func('') == '""':
|
||||||
i = i.replace('"', '\\"')
|
i = i.replace('"', '\\"')
|
||||||
newelems.append(templ % ninja_quote(i))
|
newelems.append(quoter(i))
|
||||||
line += ' '.join(newelems)
|
line += ' '.join(newelems)
|
||||||
line += '\n'
|
line += '\n'
|
||||||
outfile.write(line)
|
outfile.write(line)
|
||||||
|
@ -854,12 +855,12 @@ int dummy;
|
||||||
outfile.write(' depfile = $DEPFILE\n')
|
outfile.write(' depfile = $DEPFILE\n')
|
||||||
outfile.write(' restat = 1\n\n')
|
outfile.write(' restat = 1\n\n')
|
||||||
outfile.write('rule REGENERATE_BUILD\n')
|
outfile.write('rule REGENERATE_BUILD\n')
|
||||||
c = (quote_char + ninja_quote(sys.executable) + quote_char,
|
c = (ninja_quote(quote_func(sys.executable)),
|
||||||
quote_char + ninja_quote(self.environment.get_build_command()) + quote_char,
|
ninja_quote(quote_func(self.environment.get_build_command())),
|
||||||
'--internal',
|
'--internal',
|
||||||
'regenerate',
|
'regenerate',
|
||||||
quote_char + ninja_quote(self.environment.get_source_dir()) + quote_char,
|
ninja_quote(quote_func(self.environment.get_source_dir())),
|
||||||
quote_char + ninja_quote(self.environment.get_build_dir()) + quote_char)
|
ninja_quote(quote_func(self.environment.get_build_dir())))
|
||||||
outfile.write(" command = %s %s %s %s %s %s --backend ninja\n" % c)
|
outfile.write(" command = %s %s %s %s %s %s --backend ninja\n" % c)
|
||||||
outfile.write(' description = Regenerating build files.\n')
|
outfile.write(' description = Regenerating build files.\n')
|
||||||
outfile.write(' generator = 1\n\n')
|
outfile.write(' generator = 1\n\n')
|
||||||
|
@ -1515,7 +1516,7 @@ rule FORTRAN_DEP_HACK
|
||||||
pass
|
pass
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def generate_compile_rule_for(self, langname, compiler, qstr, is_cross, outfile):
|
def generate_compile_rule_for(self, langname, compiler, is_cross, outfile):
|
||||||
if langname == 'java':
|
if langname == 'java':
|
||||||
if not is_cross:
|
if not is_cross:
|
||||||
self.generate_java_compile_rule(compiler, outfile)
|
self.generate_java_compile_rule(compiler, outfile)
|
||||||
|
@ -1547,7 +1548,7 @@ rule FORTRAN_DEP_HACK
|
||||||
quoted_depargs = []
|
quoted_depargs = []
|
||||||
for d in depargs:
|
for d in depargs:
|
||||||
if d != '$out' and d != '$in':
|
if d != '$out' and d != '$in':
|
||||||
d = qstr % d
|
d = quote_func(d)
|
||||||
quoted_depargs.append(d)
|
quoted_depargs.append(d)
|
||||||
cross_args = self.get_cross_info_lang_args(langname, is_cross)
|
cross_args = self.get_cross_info_lang_args(langname, is_cross)
|
||||||
if mesonlib.is_windows():
|
if mesonlib.is_windows():
|
||||||
|
@ -1576,7 +1577,7 @@ rule FORTRAN_DEP_HACK
|
||||||
outfile.write(description)
|
outfile.write(description)
|
||||||
outfile.write('\n')
|
outfile.write('\n')
|
||||||
|
|
||||||
def generate_pch_rule_for(self, langname, compiler, qstr, is_cross, outfile):
|
def generate_pch_rule_for(self, langname, compiler, is_cross, outfile):
|
||||||
if langname != 'c' and langname != 'cpp':
|
if langname != 'c' and langname != 'cpp':
|
||||||
return
|
return
|
||||||
if is_cross:
|
if is_cross:
|
||||||
|
@ -1595,7 +1596,7 @@ rule FORTRAN_DEP_HACK
|
||||||
quoted_depargs = []
|
quoted_depargs = []
|
||||||
for d in depargs:
|
for d in depargs:
|
||||||
if d != '$out' and d != '$in':
|
if d != '$out' and d != '$in':
|
||||||
d = qstr % d
|
d = quote_func(d)
|
||||||
quoted_depargs.append(d)
|
quoted_depargs.append(d)
|
||||||
if compiler.get_id() == 'msvc':
|
if compiler.get_id() == 'msvc':
|
||||||
output = ''
|
output = ''
|
||||||
|
@ -1621,12 +1622,11 @@ rule FORTRAN_DEP_HACK
|
||||||
outfile.write('\n')
|
outfile.write('\n')
|
||||||
|
|
||||||
def generate_compile_rules(self, outfile):
|
def generate_compile_rules(self, outfile):
|
||||||
qstr = quote_char + "%s" + quote_char
|
|
||||||
for langname, compiler in self.build.compilers.items():
|
for langname, compiler in self.build.compilers.items():
|
||||||
if compiler.get_id() == 'clang':
|
if compiler.get_id() == 'clang':
|
||||||
self.generate_llvm_ir_compile_rule(compiler, False, outfile)
|
self.generate_llvm_ir_compile_rule(compiler, False, outfile)
|
||||||
self.generate_compile_rule_for(langname, compiler, qstr, False, outfile)
|
self.generate_compile_rule_for(langname, compiler, False, outfile)
|
||||||
self.generate_pch_rule_for(langname, compiler, qstr, False, outfile)
|
self.generate_pch_rule_for(langname, compiler, False, outfile)
|
||||||
if self.environment.is_cross_build():
|
if self.environment.is_cross_build():
|
||||||
# In case we are going a target-only build, make the native compilers
|
# In case we are going a target-only build, make the native compilers
|
||||||
# masquerade as cross compilers.
|
# masquerade as cross compilers.
|
||||||
|
@ -1637,8 +1637,8 @@ rule FORTRAN_DEP_HACK
|
||||||
for langname, compiler in cclist.items():
|
for langname, compiler in cclist.items():
|
||||||
if compiler.get_id() == 'clang':
|
if compiler.get_id() == 'clang':
|
||||||
self.generate_llvm_ir_compile_rule(compiler, True, outfile)
|
self.generate_llvm_ir_compile_rule(compiler, True, outfile)
|
||||||
self.generate_compile_rule_for(langname, compiler, qstr, True, outfile)
|
self.generate_compile_rule_for(langname, compiler, True, outfile)
|
||||||
self.generate_pch_rule_for(langname, compiler, qstr, True, outfile)
|
self.generate_pch_rule_for(langname, compiler, True, outfile)
|
||||||
outfile.write('\n')
|
outfile.write('\n')
|
||||||
|
|
||||||
def generate_generator_list_rules(self, target, outfile):
|
def generate_generator_list_rules(self, target, outfile):
|
||||||
|
|
|
@ -1266,15 +1266,15 @@ class LinuxlikeTests(BasePlatformTests):
|
||||||
self.assertIsNotNone(vala_command)
|
self.assertIsNotNone(vala_command)
|
||||||
self.assertIsNotNone(c_command)
|
self.assertIsNotNone(c_command)
|
||||||
# -w suppresses all warnings, should be there in Vala but not in C
|
# -w suppresses all warnings, should be there in Vala but not in C
|
||||||
self.assertIn("'-w'", vala_command)
|
self.assertIn(" -w ", vala_command)
|
||||||
self.assertNotIn("'-w'", c_command)
|
self.assertNotIn(" -w ", c_command)
|
||||||
# -Wall enables all warnings, should be there in C but not in Vala
|
# -Wall enables all warnings, should be there in C but not in Vala
|
||||||
self.assertNotIn("'-Wall'", vala_command)
|
self.assertNotIn(" -Wall ", vala_command)
|
||||||
self.assertIn("'-Wall'", c_command)
|
self.assertIn(" -Wall ", c_command)
|
||||||
# -Werror converts warnings to errors, should always be there since it's
|
# -Werror converts warnings to errors, should always be there since it's
|
||||||
# injected by an unrelated piece of code and the project has werror=true
|
# injected by an unrelated piece of code and the project has werror=true
|
||||||
self.assertIn("'-Werror'", vala_command)
|
self.assertIn(" -Werror ", vala_command)
|
||||||
self.assertIn("'-Werror'", c_command)
|
self.assertIn(" -Werror ", c_command)
|
||||||
|
|
||||||
def test_qt5dependency_pkgconfig_detection(self):
|
def test_qt5dependency_pkgconfig_detection(self):
|
||||||
'''
|
'''
|
||||||
|
@ -1405,7 +1405,7 @@ class LinuxlikeTests(BasePlatformTests):
|
||||||
self.init(testdir, ['-D' + std_opt])
|
self.init(testdir, ['-D' + std_opt])
|
||||||
cmd = self.get_compdb()[0]['command']
|
cmd = self.get_compdb()[0]['command']
|
||||||
if v != 'none':
|
if v != 'none':
|
||||||
cmd_std = "'-std={}'".format(v)
|
cmd_std = " -std={} ".format(v)
|
||||||
self.assertIn(cmd_std, cmd)
|
self.assertIn(cmd_std, cmd)
|
||||||
try:
|
try:
|
||||||
self.build()
|
self.build()
|
||||||
|
@ -1420,7 +1420,7 @@ class LinuxlikeTests(BasePlatformTests):
|
||||||
os.environ[env_flags] = cmd_std
|
os.environ[env_flags] = cmd_std
|
||||||
self.init(testdir)
|
self.init(testdir)
|
||||||
cmd = self.get_compdb()[0]['command']
|
cmd = self.get_compdb()[0]['command']
|
||||||
qcmd_std = "'{}'".format(cmd_std)
|
qcmd_std = " {} ".format(cmd_std)
|
||||||
self.assertIn(qcmd_std, cmd)
|
self.assertIn(qcmd_std, cmd)
|
||||||
with self.assertRaises(subprocess.CalledProcessError,
|
with self.assertRaises(subprocess.CalledProcessError,
|
||||||
msg='{} should have failed'.format(qcmd_std)):
|
msg='{} should have failed'.format(qcmd_std)):
|
||||||
|
|
Loading…
Reference in New Issue