Merge pull request #641 from centricular/allow-backslash
Ninja now supports backslash in command args, so we can too.
This commit is contained in:
commit
933c11821e
|
@ -296,6 +296,33 @@ class Backend():
|
||||||
args = includeargs + args
|
args = includeargs + args
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape_extra_args(compiler, args):
|
||||||
|
# No extra escaping/quoting needed when not running on Windows
|
||||||
|
if not mesonlib.is_windows():
|
||||||
|
return args
|
||||||
|
extra_args = []
|
||||||
|
# Compiler-specific escaping is needed for -D args but not for any others
|
||||||
|
if compiler.get_id() == 'msvc':
|
||||||
|
# MSVC needs escaping when a -D argument ends in \ or \"
|
||||||
|
for arg in args:
|
||||||
|
if arg.startswith('-D') or arg.startswith('/D'):
|
||||||
|
# Without extra escaping for these two, the next character
|
||||||
|
# gets eaten
|
||||||
|
if arg.endswith('\\'):
|
||||||
|
arg += '\\'
|
||||||
|
elif arg.endswith('\\"'):
|
||||||
|
arg = arg[:-2] + '\\\\"'
|
||||||
|
extra_args.append(arg)
|
||||||
|
else:
|
||||||
|
# MinGW GCC needs all backslashes in defines to be doubly-escaped
|
||||||
|
# FIXME: Not sure about Cygwin or Clang
|
||||||
|
for arg in args:
|
||||||
|
if arg.startswith('-D') or arg.startswith('/D'):
|
||||||
|
arg = arg.replace('\\', '\\\\')
|
||||||
|
extra_args.append(arg)
|
||||||
|
return extra_args
|
||||||
|
|
||||||
def generate_basic_compiler_args(self, target, compiler):
|
def generate_basic_compiler_args(self, target, compiler):
|
||||||
commands = []
|
commands = []
|
||||||
commands += self.get_cross_stdlib_args(target, compiler)
|
commands += self.get_cross_stdlib_args(target, compiler)
|
||||||
|
@ -304,7 +331,7 @@ class Backend():
|
||||||
commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options)
|
commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options)
|
||||||
commands += self.build.get_global_args(compiler)
|
commands += self.build.get_global_args(compiler)
|
||||||
commands += self.environment.coredata.external_args[compiler.get_language()]
|
commands += self.environment.coredata.external_args[compiler.get_language()]
|
||||||
commands += target.get_extra_args(compiler.get_language())
|
commands += self.escape_extra_args(compiler, target.get_extra_args(compiler.get_language()))
|
||||||
commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
|
commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype'))
|
||||||
if self.environment.coredata.get_builtin_option('werror'):
|
if self.environment.coredata.get_builtin_option('werror'):
|
||||||
commands += compiler.get_werror_args()
|
commands += compiler.get_werror_args()
|
||||||
|
|
|
@ -436,9 +436,30 @@ class Vs2010Backend(backends.Backend):
|
||||||
# they are part of the CustomBuildStep Outputs.
|
# they are part of the CustomBuildStep Outputs.
|
||||||
return
|
return
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def quote_define_cmdline(cls, arg):
|
def escape_preprocessor_define(define):
|
||||||
return re.sub(r'^([-/])D(.*?)="(.*)"$', r'\1D\2=\"\3\"', arg)
|
# See: https://msdn.microsoft.com/en-us/library/bb383819.aspx
|
||||||
|
table = str.maketrans({'%': '%25', '$': '%24', '@': '%40',
|
||||||
|
"'": '%27', ';': '%3B', '?': '%3F', '*': '%2A',
|
||||||
|
# We need to escape backslash because it'll be un-escaped by
|
||||||
|
# Windows during process creation when it parses the arguments
|
||||||
|
# Basically, this converts `\` to `\\`.
|
||||||
|
'\\': '\\\\'})
|
||||||
|
return define.translate(table)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape_additional_option(option):
|
||||||
|
# See: https://msdn.microsoft.com/en-us/library/bb383819.aspx
|
||||||
|
table = str.maketrans({'%': '%25', '$': '%24', '@': '%40',
|
||||||
|
"'": '%27', ';': '%3B', '?': '%3F', '*': '%2A', ' ': '%20',})
|
||||||
|
option = option.translate(table)
|
||||||
|
# Since we're surrounding the option with ", if it ends in \ that will
|
||||||
|
# escape the " when the process arguments are parsed and the starting
|
||||||
|
# " will not terminate. So we escape it if that's the case. I'm not
|
||||||
|
# kidding, this is how escaping works for process args on Windows.
|
||||||
|
if option.endswith('\\'):
|
||||||
|
option += '\\'
|
||||||
|
return '"{}"'.format(option)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def split_link_args(args):
|
def split_link_args(args):
|
||||||
|
@ -633,7 +654,7 @@ class Vs2010Backend(backends.Backend):
|
||||||
# so filter them out if needed
|
# so filter them out if needed
|
||||||
d_compile_args = compiler.unix_compile_flags_to_native(d.get_compile_args())
|
d_compile_args = compiler.unix_compile_flags_to_native(d.get_compile_args())
|
||||||
for arg in d_compile_args:
|
for arg in d_compile_args:
|
||||||
if arg.startswith('-I'):
|
if arg.startswith('-I') or arg.startswith('/I'):
|
||||||
inc_dir = arg[2:]
|
inc_dir = arg[2:]
|
||||||
# De-dup
|
# De-dup
|
||||||
if inc_dir not in inc_dirs:
|
if inc_dir not in inc_dirs:
|
||||||
|
@ -641,8 +662,24 @@ class Vs2010Backend(backends.Backend):
|
||||||
else:
|
else:
|
||||||
general_args.append(arg)
|
general_args.append(arg)
|
||||||
|
|
||||||
|
defines = []
|
||||||
|
# Split preprocessor defines and include directories out of the list of
|
||||||
|
# all extra arguments. The rest go into %(AdditionalOptions).
|
||||||
for l, args in extra_args.items():
|
for l, args in extra_args.items():
|
||||||
extra_args[l] = [Vs2010Backend.quote_define_cmdline(x) for x in args]
|
extra_args[l] = []
|
||||||
|
for arg in args:
|
||||||
|
if arg.startswith('-D') or arg.startswith('/D'):
|
||||||
|
define = self.escape_preprocessor_define(arg[2:])
|
||||||
|
# De-dup
|
||||||
|
if define not in defines:
|
||||||
|
defines.append(define)
|
||||||
|
elif arg.startswith('-I') or arg.startswith('/I'):
|
||||||
|
inc_dir = arg[2:]
|
||||||
|
# De-dup
|
||||||
|
if inc_dir not in inc_dirs:
|
||||||
|
inc_dirs.append(inc_dir)
|
||||||
|
else:
|
||||||
|
extra_args[l].append(self.escape_additional_option(arg))
|
||||||
|
|
||||||
languages += gen_langs
|
languages += gen_langs
|
||||||
has_language_specific_args = any(l != extra_args['c'] for l in extra_args.values())
|
has_language_specific_args = any(l != extra_args['c'] for l in extra_args.values())
|
||||||
|
@ -669,7 +706,7 @@ class Vs2010Backend(backends.Backend):
|
||||||
|
|
||||||
inc_dirs.append('%(AdditionalIncludeDirectories)')
|
inc_dirs.append('%(AdditionalIncludeDirectories)')
|
||||||
ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(inc_dirs)
|
ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(inc_dirs)
|
||||||
preproc = ET.SubElement(clconf, 'PreprocessorDefinitions')
|
ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(defines)
|
||||||
rebuild = ET.SubElement(clconf, 'MinimalRebuild')
|
rebuild = ET.SubElement(clconf, 'MinimalRebuild')
|
||||||
rebuild.text = 'true'
|
rebuild.text = 'true'
|
||||||
funclink = ET.SubElement(clconf, 'FunctionLevelLinking')
|
funclink = ET.SubElement(clconf, 'FunctionLevelLinking')
|
||||||
|
|
|
@ -50,28 +50,6 @@ known_shlib_kwargs.update({'version' : True,
|
||||||
'name_suffix' : True,
|
'name_suffix' : True,
|
||||||
'vs_module_defs' : True})
|
'vs_module_defs' : True})
|
||||||
|
|
||||||
backslash_explanation = \
|
|
||||||
'''Compiler arguments have a backslash "\\" character. This is unfortunately not
|
|
||||||
permitted. The reason for this is that backslash is a shell quoting character
|
|
||||||
that behaves differently across different systems. Because of this is it not
|
|
||||||
possible to make it work reliably across all the platforms Meson needs to
|
|
||||||
support.
|
|
||||||
|
|
||||||
There are several different ways of working around this issue. Most of the time
|
|
||||||
you are using this to provide a -D define to your compiler. Try instead to
|
|
||||||
create a config.h file and put all of your definitions in it using
|
|
||||||
configure_file().
|
|
||||||
|
|
||||||
Another approach is to move the backslashes into the source and have the other
|
|
||||||
bits in the def. So you would have an arg -DPLAIN_TEXT="foo" and then in your
|
|
||||||
C sources something like this:
|
|
||||||
|
|
||||||
const char *fulltext = "\\\\" PLAIN_TEXT;
|
|
||||||
|
|
||||||
We are fully aware that these are not really usable or pleasant ways to do
|
|
||||||
this but it's the best we can do given the way shell quoting works.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def sources_are_suffix(sources, suffix):
|
def sources_are_suffix(sources, suffix):
|
||||||
for source in sources:
|
for source in sources:
|
||||||
if source.endswith('.' + suffix):
|
if source.endswith('.' + suffix):
|
||||||
|
@ -606,8 +584,6 @@ class BuildTarget():
|
||||||
for a in args:
|
for a in args:
|
||||||
if not isinstance(a, (str, File)):
|
if not isinstance(a, (str, File)):
|
||||||
raise InvalidArguments('A non-string passed to compiler args.')
|
raise InvalidArguments('A non-string passed to compiler args.')
|
||||||
if isinstance(a, str) and '\\' in a:
|
|
||||||
raise InvalidArguments(backslash_explanation)
|
|
||||||
if language in self.extra_args:
|
if language in self.extra_args:
|
||||||
self.extra_args[language] += args
|
self.extra_args[language] += args
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
configure_file(output : 'blank.txt', configuration : configuration_data())
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "comparer.h"
|
||||||
|
|
||||||
|
#ifndef COMPARER_INCLUDED
|
||||||
|
#error "comparer.h not included"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This converts foo\\\\bar\\\\ to "foo\\bar\\" (string literal) */
|
||||||
|
#define Q(x) #x
|
||||||
|
#define QUOTE(x) Q(x)
|
||||||
|
|
||||||
|
#define COMPARE_WITH "foo\\bar\\" /* This is the literal `foo\bar\` */
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if(strcmp(QUOTE(DEF_WITH_BACKSLASH), COMPARE_WITH)) {
|
||||||
|
printf("Arg string is quoted incorrectly: %s instead of %s\n",
|
||||||
|
QUOTE(DEF_WITH_BACKSLASH), COMPARE_WITH);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "comparer.h"
|
||||||
|
|
||||||
|
#ifndef COMPARER_INCLUDED
|
||||||
|
#error "comparer.h not included"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COMPARE_WITH "foo\\bar\\" /* This is `foo\bar\` */
|
||||||
|
|
||||||
|
int main (int argc, char **argv) {
|
||||||
|
if (strcmp (DEF_WITH_BACKSLASH, COMPARE_WITH)) {
|
||||||
|
printf ("Arg string is quoted incorrectly: %s vs %s\n",
|
||||||
|
DEF_WITH_BACKSLASH, COMPARE_WITH);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "comparer.h"
|
||||||
|
|
||||||
|
#ifndef COMPARER_INCLUDED
|
||||||
|
#error "comparer.h not included"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define COMPARE_WITH "foo\\bar" /* This is the literal `foo\bar` */
|
||||||
|
|
||||||
|
int main (int argc, char **argv) {
|
||||||
|
if (strcmp (DEF_WITH_BACKSLASH, COMPARE_WITH)) {
|
||||||
|
printf ("Arg string is quoted incorrectly: %s instead of %s\n",
|
||||||
|
DEF_WITH_BACKSLASH, COMPARE_WITH);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define COMPARER_INCLUDED
|
|
@ -0,0 +1,28 @@
|
||||||
|
project('comparer', 'c')
|
||||||
|
|
||||||
|
# Added manually as a c_arg to test handling of include paths with backslashes
|
||||||
|
# and spaces. This is especially useful on Windows in vcxproj files since it
|
||||||
|
# stores include directories in a separate element that has its own
|
||||||
|
# context-specific escaping/quoting.
|
||||||
|
include_dir = meson.current_source_dir() + '/include'
|
||||||
|
default_c_args = ['-I' + include_dir]
|
||||||
|
|
||||||
|
if meson.get_compiler('c').get_id() == 'msvc'
|
||||||
|
default_c_args += ['/Faasm output\\']
|
||||||
|
# Hack to create the 'asm output' directory in the builddir
|
||||||
|
subdir('asm output')
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Path can contain \. Here we're sending `"foo\bar"`.
|
||||||
|
test('backslash quoting',
|
||||||
|
executable('comparer', 'comparer.c',
|
||||||
|
c_args : default_c_args + ['-DDEF_WITH_BACKSLASH="foo\\bar"']))
|
||||||
|
# Path can end in \ without any special quoting. Here we send `"foo\bar\"`.
|
||||||
|
test('backslash end quoting',
|
||||||
|
executable('comparer-end', 'comparer-end.c',
|
||||||
|
c_args : default_c_args + ['-DDEF_WITH_BACKSLASH="foo\\bar\\"']))
|
||||||
|
# Path can (really) end in \ if we're not passing a string literal without any
|
||||||
|
# special quoting. Here we're sending `foo\bar\`.
|
||||||
|
test('backslash end quoting when not a string literal',
|
||||||
|
executable('comparer-end-notstring', 'comparer-end-notstring.c',
|
||||||
|
c_args : default_c_args + ['-DDEF_WITH_BACKSLASH=foo\\bar\\']))
|
|
@ -1,10 +0,0 @@
|
||||||
#include<string.h>
|
|
||||||
#include<stdio.h>
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if(strcmp(DEF_WITH_BACKSLASH, "foo\\bar")) {
|
|
||||||
printf("Arg string is quoted incorrectly: %s\n", DEF_WITH_BACKSLASH);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
project('comparer', 'c')
|
|
||||||
|
|
||||||
test('backslash quoting', executable('comparer', 'comparer.c', c_args : '-DDEF_WITH_BACKSLASH="foo\\bar"'))
|
|
Loading…
Reference in New Issue