Compare commits

...

8 Commits

Author SHA1 Message Date
dundir 61b5361c62 Update Quickstart Guide [skip ci] 5 years ago
Jussi Pakkanen d67d5411bb
Merge pull request #5823 from scivision/linker-pgi-linux 5 years ago
Fabian Bläse 7b76515534 man: Make argument order of setup command consistent with help message 5 years ago
Aleksey Gurtovoy 75daed27bc mesonlib.split_args/quote_arg/join_args 5 years ago
Michael Hirsch, Ph.D b9af8f8b6b
PGI cannot accept -pthread 5 years ago
Michael Hirsch, Ph.D 25653cac07
PGI: windows workaround not-msvc-like enough function 5 years ago
Michael Hirsch, Ph.D 3447767cac
PGI: windows PGI is sort of MSVC-like 5 years ago
Michael Hirsch, Ph.D c8d380e4ef
PGI: use ar link wrapper on Windows 5 years ago
  1. 74
      docs/markdown/Quick-guide.md
  2. 8
      man/meson.1
  3. 14
      mesonbuild/backend/ninjabackend.py
  4. 14
      mesonbuild/compilers/compilers.py
  5. 3
      mesonbuild/compilers/mixins/islinker.py
  6. 6
      mesonbuild/compilers/mixins/pgi.py
  7. 12
      mesonbuild/coredata.py
  8. 18
      mesonbuild/dependencies/base.py
  9. 8
      mesonbuild/dependencies/misc.py
  10. 6
      mesonbuild/envconfig.py
  11. 15
      mesonbuild/environment.py
  12. 19
      mesonbuild/linkers.py
  13. 80
      mesonbuild/mesonlib.py
  14. 11
      mesonbuild/modules/gnome.py
  15. 2
      mesonbuild/modules/pkgconfig.py
  16. 7
      mesonbuild/mtest.py
  17. 5
      mesonbuild/scripts/gtkdochelper.py
  18. 5
      mesonbuild/scripts/scanbuild.py
  19. 3
      run_project_tests.py
  20. 2
      run_tests.py
  21. 116
      run_unittests.py

@ -1,44 +1,86 @@
---
title: Quick guide
short-description: Guide to get started using meson
title: Quickstart Guide
short-description: Getting Started using Mesonbuild
...
# Using Meson
Meson has been designed to be as easy to use as possible. This page
outlines the basic use cases. For more advanced cases refer to Meson's
command line help which is accessible with the command `meson --help`.
Meson has been designed to be as simple to use as possible. This page
outlines the initial steps needed for installation, troubleshooting,
and standard use.
For more advanced configuration please refer to the command line help `meson --help`
or the Meson documentation located at the [Mesonbuild](https://mesonbuild.com) website.
Table of Contents:
* [Requirements](#requirements)
* [Installation using package manager](#installation-using-package-manager)
* [Installation using Python](#installation-using-python)
* [Installation from source](#installation-from-source)
* [Troubleshooting](#troubleshooting)
* [Compiling a Meson project](#compiling-a-meson-project)
* [Using Meson as a distro packager](#using-meson-as-a-distro-packager)
Requirements
--
Meson has two main dependencies.
* [Python 3](https://python.org)
* [Ninja](https://github.com/ninja-build/ninja/)
Ninja is only needed if you use the Ninja backend. Meson can also
generate native VS and XCode project files.
*Ninja is only needed if you use the Ninja backend. Meson can also
generate native VS and XCode project files.*
Installation using package manager
--
On Ubuntu these can be easily installed with the following command:
Ubuntu:
```console
$ sudo apt-get install python3 python3-pip python3-setuptools \
python3-wheel ninja-build
```
*Due to our frequent release cycle and development speed, distro packaged software may quickly become outdated.*
Installation using Python
--
Requirements: **pip3**
The best way to get Meson is to `pip install` it for your user
The best way to receive the most up-to-date version of Mesonbuild.
Install as a local user (recommended):
```console
$ pip3 install --user meson
```
Install as root:
```console
$ pip3 install meson
```
*If you are unsure whether to install as root or a local user, install as a local user.*
Installation from source
--
Requirements: **git**
Meson can be run directly from the cloned git repository.
```console
$ git clone https://github.com/mesonbuild/meson.git /path/to/sourcedir
```
Troubleshooting:
--
Common Issues:
```console
$ meson builddir
$ bash: /usr/bin/meson: No such file or directory
```
Description: The default installation prefix for the python pip module installation is not included in your shell environment PATH. The default prefix for python pip installation modules is located under ``/usr/local``.
**Resolution:
This issue can be resolved by altering the default shell environment PATH to include ``/usr/local/bin``. **
You can also use Meson as packaged by your distro, but beware that due
to our frequent release cycle and development speed this version might
be out of date.
*Note: There are other ways of fixing this issue such as using symlinks or copying the binaries to a default path and these methods are not recommended or supported as they may break package management interoperability.*
Another option is to clone the git repository and run it directly from
there.
Compiling a Meson project
--

@ -20,9 +20,9 @@ configure your build:
.B meson setup [
.I options
.B ] [
.I source directory
.B ] [
.I build directory
.B ] [
.I source directory
.B ]
Note that the build directory must be different from the source
@ -48,9 +48,9 @@ like this:
.B meson [
.I options
.B ] [
.I source directory
.B ] [
.I build directory
.B ] [
.I source directory
.B ]
.SS "options:"

@ -14,7 +14,6 @@
from typing import List
import os
import re
import shlex
import pickle
import subprocess
from collections import OrderedDict
@ -29,10 +28,11 @@ from .. import build
from .. import mlog
from .. import dependencies
from .. import compilers
from ..compilers import Compiler, CompilerArgs, CCompiler, VisualStudioLikeCompiler, FortranCompiler
from ..compilers import (Compiler, CompilerArgs, CCompiler, FortranCompiler,
PGICCompiler, VisualStudioLikeCompiler)
from ..linkers import ArLinker
from ..mesonlib import (
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine, ProgressBar
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine, ProgressBar, quote_arg
)
from ..mesonlib import get_compiler_for_source, has_path_sep
from .backends import CleanTrees
@ -44,11 +44,14 @@ FORTRAN_SUBMOD_PAT = r"^\s*\bsubmodule\b\s*\((\w+:?\w+)\)\s*(\w+)"
FORTRAN_USE_PAT = r"^\s*use,?\s*(?:non_intrinsic)?\s*(?:::)?\s*(\w+)"
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']
rmfile_prefix = ['del', '/f', '/s', '/q', '{}', '&&']
else:
quote_func = shlex.quote
quote_func = quote_arg
execute_wrapper = []
rmfile_prefix = ['rm', '-f', '{}', '&&']
@ -232,6 +235,9 @@ class NinjaBackend(backends.Backend):
# IFort on windows is MSVC like, but doesn't have /showincludes
if isinstance(compiler, FortranCompiler):
continue
if isinstance(compiler, PGICCompiler) and mesonlib.is_windows():
# for the purpose of this function, PGI doesn't act enough like MSVC
return open(tempfilename, 'a', encoding='utf-8')
if isinstance(compiler, VisualStudioLikeCompiler):
break
else:

@ -12,9 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import contextlib, enum, os.path, re, tempfile, shlex
import contextlib, enum, os.path, re, tempfile
import typing
from typing import List, Optional, Tuple
from typing import Optional, Tuple, List
from ..linkers import StaticLinker, GnuLikeDynamicLinkerMixin
from .. import coredata
@ -22,7 +22,7 @@ from .. import mlog
from .. import mesonlib
from ..mesonlib import (
EnvironmentException, MachineChoice, MesonException, OrderedSet,
Popen_safe
Popen_safe, split_args
)
from ..envconfig import (
Properties,
@ -799,7 +799,7 @@ class Compiler:
env_compile_flags = os.environ.get(cflags_mapping[lang])
log_var(cflags_mapping[lang], env_compile_flags)
if env_compile_flags is not None:
compile_flags += shlex.split(env_compile_flags)
compile_flags += split_args(env_compile_flags)
# Link flags (same for all languages)
if self.use_ldflags():
@ -820,7 +820,7 @@ class Compiler:
env_preproc_flags = os.environ.get('CPPFLAGS')
log_var('CPPFLAGS', env_preproc_flags)
if env_preproc_flags is not None:
compile_flags += shlex.split(env_preproc_flags)
compile_flags += split_args(env_preproc_flags)
return compile_flags, link_flags
@ -830,10 +830,10 @@ class Compiler:
opts.update({
self.language + '_args': coredata.UserArrayOption(
description + ' compiler',
[], shlex_split=True, user_input=True, allow_dups=True),
[], split_args=True, user_input=True, allow_dups=True),
self.language + '_link_args': coredata.UserArrayOption(
description + ' linker',
[], shlex_split=True, user_input=True, allow_dups=True),
[], split_args=True, user_input=True, allow_dups=True),
})
return opts

@ -21,7 +21,6 @@ classes for those cases.
"""
import os
import shlex
import typing
from ... import mesonlib
@ -39,7 +38,7 @@ class LinkerEnvVarsMixin:
flags = os.environ.get('LDFLAGS')
if not flags:
return []
return shlex.split(flags)
return mesonlib.split_args(flags)
class BasicLinkerIsCompilerMixin:

@ -56,7 +56,7 @@ class PGICompiler:
def get_pic_args(self) -> typing.List[str]:
# PGI -fPIC is Linux only.
if self.compiler_type.is_linux_compiler():
if self.compiler_type.is_standard_compiler:
return ['-fPIC']
return []
@ -97,3 +97,7 @@ class PGICompiler:
'-I{}'.format(hdr.parent)]
else:
return []
def thread_flags(self, env):
# PGI cannot accept -pthread, it's already threaded
return []

@ -13,14 +13,14 @@
# limitations under the License.
from . import mlog
import pickle, os, uuid, shlex
import pickle, os, uuid
import sys
from itertools import chain
from pathlib import PurePath
from collections import OrderedDict
from .mesonlib import (
MesonException, MachineChoice, PerMachine,
default_libdir, default_libexecdir, default_prefix
default_libdir, default_libexecdir, default_prefix, split_args
)
from .wrap import WrapMode
import ast
@ -163,9 +163,9 @@ class UserComboOption(UserOption[str]):
return value
class UserArrayOption(UserOption[List[str]]):
def __init__(self, description, value, shlex_split=False, user_input=False, allow_dups=False, **kwargs):
def __init__(self, description, value, split_args=False, user_input=False, allow_dups=False, **kwargs):
super().__init__(description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None))
self.shlex_split = shlex_split
self.split_args = split_args
self.allow_dups = allow_dups
self.value = self.validate_value(value, user_input=user_input)
@ -183,8 +183,8 @@ class UserArrayOption(UserOption[List[str]]):
elif value == '':
newvalue = []
else:
if self.shlex_split:
newvalue = shlex.split(value)
if self.split_args:
newvalue = split_args(value)
else:
newvalue = [v.strip() for v in value.split(',')]
elif isinstance(value, list):

@ -34,7 +34,7 @@ from ..compilers import clib_langs
from ..environment import BinaryTable, Environment, MachineInfo
from ..cmake import CMakeExecutor, CMakeTraceParser, CMakeException
from ..mesonlib import MachineChoice, MesonException, OrderedSet, PerMachine
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list
from ..mesonlib import Popen_safe, version_compare_many, version_compare, listify, stringlistify, extract_as_list, split_args
from ..mesonlib import Version, LibType
# These must be defined in this file to avoid cyclical references.
@ -490,16 +490,13 @@ class ConfigToolDependency(ExternalDependency):
def get_config_value(self, args, stage):
p, out, err = Popen_safe(self.config + args)
# This is required to keep shlex from stripping path separators on
# Windows. Also, don't put escape sequences in config values, okay?
out = out.replace('\\', '\\\\')
if p.returncode != 0:
if self.required:
raise DependencyException(
'Could not generate {} for {}.\n{}'.format(
stage, self.name, err))
return []
return shlex.split(out)
return split_args(out)
@staticmethod
def get_methods():
@ -697,6 +694,11 @@ class PkgConfigDependency(ExternalDependency):
converted.append(arg)
return converted
def _split_args(self, cmd):
# pkg-config paths follow Unix conventions, even on Windows; split the
# output using shlex.split rather than mesonlib.split_args
return shlex.split(cmd)
def _set_cargs(self):
env = None
if self.language == 'fortran':
@ -708,7 +710,7 @@ class PkgConfigDependency(ExternalDependency):
if ret != 0:
raise DependencyException('Could not generate cargs for %s:\n\n%s' %
(self.name, out))
self.compile_args = self._convert_mingw_paths(shlex.split(out))
self.compile_args = self._convert_mingw_paths(self._split_args(out))
def _search_libs(self, out, out_raw):
'''
@ -737,7 +739,7 @@ class PkgConfigDependency(ExternalDependency):
# always searched first.
prefix_libpaths = OrderedSet()
# We also store this raw_link_args on the object later
raw_link_args = self._convert_mingw_paths(shlex.split(out_raw))
raw_link_args = self._convert_mingw_paths(self._split_args(out_raw))
for arg in raw_link_args:
if arg.startswith('-L') and not arg.startswith(('-L-l', '-L-L')):
path = arg[2:]
@ -746,7 +748,7 @@ class PkgConfigDependency(ExternalDependency):
path = os.path.join(self.env.get_build_dir(), path)
prefix_libpaths.add(path)
system_libpaths = OrderedSet()
full_args = self._convert_mingw_paths(shlex.split(out))
full_args = self._convert_mingw_paths(self._split_args(out))
for arg in full_args:
if arg.startswith(('-L-l', '-L-L')):
# These are D language arguments, not library paths

@ -18,11 +18,11 @@ from pathlib import Path
import functools
import os
import re
import shlex
import sysconfig
from .. import mlog
from .. import mesonlib
from ..mesonlib import split_args
from ..environment import detect_cpu_family
from .base import (
@ -277,7 +277,7 @@ class MPIDependency(ExternalDependency):
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
cargs = shlex.split(o)
cargs = split_args(o)
cmd = prog.get_command() + ['--showme:link']
p, o, e = mesonlib.Popen_safe(cmd)
@ -287,7 +287,7 @@ class MPIDependency(ExternalDependency):
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
libs = shlex.split(o)
libs = split_args(o)
cmd = prog.get_command() + ['--showme:version']
p, o, e = mesonlib.Popen_safe(cmd)
@ -316,7 +316,7 @@ class MPIDependency(ExternalDependency):
mlog.debug(mlog.bold('Standard output\n'), o)
mlog.debug(mlog.bold('Standard error\n'), e)
return
args = shlex.split(o)
args = split_args(o)
version = None

@ -12,11 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import configparser, os, shlex, subprocess
import configparser, os, subprocess
import typing
from . import mesonlib
from .mesonlib import EnvironmentException
from .mesonlib import EnvironmentException, split_args
from . import mlog
_T = typing.TypeVar('_T')
@ -361,7 +361,7 @@ This is probably wrong, it should always point to the native compiler.''' % evar
evar = self.evarMap.get(name, "")
command = os.environ.get(evar)
if command is not None:
command = shlex.split(command)
command = split_args(command)
# Do not return empty or blank string entries
if command is not None and (len(command) == 0 or len(command[0].strip()) == 0):

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os, platform, re, sys, shlex, shutil, subprocess, typing
import os, platform, re, sys, shutil, subprocess, typing
import tempfile
from . import coredata
@ -20,7 +20,7 @@ from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker, CcrxLin
from . import mesonlib
from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, Popen_safe,
PerMachineDefaultable, PerThreeMachineDefaultable
PerMachineDefaultable, PerThreeMachineDefaultable, split_args, quote_arg
)
from . import mlog
@ -51,6 +51,7 @@ from .linkers import (
MSVCDynamicLinker,
OptlinkDynamicLinker,
PGIDynamicLinker,
PGIStaticLinker,
SolarisDynamicLinker,
XildAppleDynamicLinker,
XildLinuxDynamicLinker,
@ -120,7 +121,7 @@ def detect_gcovr(min_version='3.3', new_rootdir_version='4.2', log=False):
found = search_version(found)
if p.returncode == 0 and mesonlib.version_compare(found, '>=' + min_version):
if log:
mlog.log('Found gcovr-{} at {}'.format(found, shlex.quote(shutil.which(gcovr_exe))))
mlog.log('Found gcovr-{} at {}'.format(found, quote_arg(shutil.which(gcovr_exe))))
return gcovr_exe, mesonlib.version_compare(found, '>=' + new_rootdir_version)
return None, None
@ -158,7 +159,7 @@ def detect_ninja(version: str = '1.5', log: bool = False) -> str:
name = 'ninja'
if name == 'samu':
name = 'samurai'
mlog.log('Found {}-{} at {}'.format(name, found, shlex.quote(n)))
mlog.log('Found {}-{} at {}'.format(name, found, quote_arg(n)))
return n
def detect_native_windows_arch():
@ -1322,7 +1323,7 @@ class Environment:
if isinstance(compiler, compilers.CudaCompiler):
linkers = [self.cuda_static_linker, self.default_static_linker]
elif evar in os.environ:
linkers = [shlex.split(os.environ[evar])]
linkers = [split_args(os.environ[evar])]
elif isinstance(compiler, compilers.VisualStudioLikeCompiler):
linkers = [self.vs_static_linker, self.clang_cl_static_linker]
elif isinstance(compiler, compilers.GnuCompiler):
@ -1340,6 +1341,8 @@ class Environment:
elif isinstance(compiler, IntelClCCompiler):
# Intel has it's own linker that acts like microsoft's lib
linkers = ['xilib']
elif isinstance(compiler, (PGICCompiler, PGIFortranCompiler)) and mesonlib.is_windows():
linkers = [self.default_static_linker] # this is just a wrapper calling link/lib on Windows, keeping things simple.
else:
linkers = [self.default_static_linker]
popen_exceptions = {}
@ -1357,6 +1360,8 @@ class Environment:
return IntelVisualStudioLinker(linker, getattr(compiler, 'machine', None))
if '/OUT:' in out.upper() or '/OUT:' in err.upper():
return VisualStudioLinker(linker, getattr(compiler, 'machine', None))
if 'ar-Error-Unknown switch: --version' in err:
return PGIStaticLinker(linker)
if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
return ArmarLinker(linker)
if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out:

@ -14,7 +14,6 @@
import abc
import os
import shlex
import typing
from . import mesonlib
@ -279,7 +278,7 @@ class DynamicLinker(metaclass=abc.ABCMeta):
flags = os.environ.get('LDFLAGS')
if not flags:
return []
return shlex.split(flags)
return mesonlib.split_args(flags)
def get_option_args(self, options: 'OptionDictType') -> typing.List[str]:
return []
@ -740,18 +739,30 @@ class PGIDynamicLinker(PosixDynamicLinkerMixin, DynamicLinker):
# PGI -shared is Linux only.
if mesonlib.is_windows():
return ['-Bdynamic', '-Mmakedll']
elif mesonlib.is_linux:
elif mesonlib.is_linux():
return ['-shared']
return []
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
rpath_paths: str, build_rpath: str,
install_rpath: str) -> typing.List[str]:
if env.machines[self.for_machine].is_windows():
if not env.machines[self.for_machine].is_windows():
return ['-R' + os.path.join(build_dir, p) for p in rpath_paths]
return []
class PGIStaticLinker(StaticLinker):
def __init__(self, exelist: typing.List[str]):
super().__init__(exelist)
self.id = 'ar'
self.std_args = ['-r']
def get_std_link_args(self) -> typing.List[str]:
return self.std_args
def get_output_args(self, target: str) -> typing.List[str]:
return [target]
class VisualStudioLikeLinkerMixin:
_BUILDTYPE_ARGS = {

@ -17,7 +17,7 @@ from pathlib import Path
import sys
import stat
import time
import platform, subprocess, operator, os, shutil, re
import platform, subprocess, operator, os, shlex, shutil, re
import collections
from enum import Enum
from functools import lru_cache
@ -729,6 +729,84 @@ def has_path_sep(name, sep='/\\'):
return True
return False
if is_windows():
# shlex.split is not suitable for splitting command line on Window (https://bugs.python.org/issue1724822);
# shlex.quote is similarly problematic. Below are "proper" implementations of these functions according to
# https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments and
# https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
_whitespace = ' \t\n\r'
_find_unsafe_char = re.compile(r'[{}"]'.format(_whitespace)).search
def quote_arg(arg):
if arg and not _find_unsafe_char(arg):
return arg
result = '"'
num_backslashes = 0
for c in arg:
if c == '\\':
num_backslashes += 1
else:
if c == '"':
# Escape all backslashes and the following double quotation mark
num_backslashes = num_backslashes * 2 + 1
result += num_backslashes * '\\' + c
num_backslashes = 0
# Escape all backslashes, but let the terminating double quotation
# mark we add below be interpreted as a metacharacter
result += (num_backslashes * 2) * '\\' + '"'
return result
def split_args(cmd):
result = []
arg = ''
num_backslashes = 0
num_quotes = 0
in_quotes = False
for c in cmd:
if c == '\\':
num_backslashes += 1
else:
if c == '"' and not (num_backslashes % 2):
# unescaped quote, eat it
arg += (num_backslashes // 2) * '\\'
num_quotes += 1
in_quotes = not in_quotes
elif c in _whitespace and not in_quotes:
if arg or num_quotes:
# reached the end of the argument
result.append(arg)
arg = ''
num_quotes = 0
else:
if c == '"':
# escaped quote
num_backslashes = (num_backslashes - 1) // 2
arg += num_backslashes * '\\' + c
num_backslashes = 0
if arg or num_quotes:
result.append(arg)
return result
else:
def quote_arg(arg):
return shlex.quote(arg)
def split_args(cmd):
return shlex.split(cmd)
def join_args(args):
return ' '.join([quote_arg(x) for x in args])
def do_replacement(regex, line, variable_format, confdata):
missing_variables = set()
start_tag = '@'

@ -17,7 +17,6 @@ functionality such as gobject-introspection, gresources and gtk-doc'''
import os
import copy
import shlex
import subprocess
from .. import build
@ -29,7 +28,7 @@ from . import get_include_args
from . import ExtensionModule
from . import ModuleReturnValue
from ..mesonlib import (
MachineChoice, MesonException, OrderedSet, Popen_safe, extract_as_list
MachineChoice, MesonException, OrderedSet, Popen_safe, extract_as_list, join_args
)
from ..dependencies import Dependency, PkgConfigDependency, InternalDependency
from ..interpreterbase import noKwargs, permittedKwargs, FeatureNew, FeatureNewKwargs
@ -1079,12 +1078,12 @@ This will become a hard error in the future.''')
ldflags.extend(compiler_flags[1])
ldflags.extend(compiler_flags[2])
if compiler:
args += ['--cc=%s' % ' '.join([shlex.quote(x) for x in compiler.get_exelist()])]
args += ['--ld=%s' % ' '.join([shlex.quote(x) for x in compiler.get_linker_exelist()])]
args += ['--cc=%s' % join_args(compiler.get_exelist())]
args += ['--ld=%s' % join_args(compiler.get_linker_exelist())]
if cflags:
args += ['--cflags=%s' % ' '.join([shlex.quote(x) for x in cflags])]
args += ['--cflags=%s' % join_args(cflags)]
if ldflags:
args += ['--ldflags=%s' % ' '.join([shlex.quote(x) for x in ldflags])]
args += ['--ldflags=%s' % join_args(ldflags)]
return args

@ -240,7 +240,7 @@ class PkgConfigModule(ExtensionModule):
def _escape(self, value):
'''
We cannot use shlex.quote because it quotes with ' and " which does not
We cannot use quote_arg because it quotes with ' and " which does not
work with pkg-config and pkgconf at all.
'''
# We should always write out paths with / because pkg-config requires

@ -29,7 +29,6 @@ import pickle
import platform
import random
import re
import shlex
import signal
import subprocess
import sys
@ -41,7 +40,7 @@ from . import build
from . import environment
from . import mlog
from .dependencies import ExternalProgram
from .mesonlib import MesonException, get_wine_shortpath
from .mesonlib import MesonException, get_wine_shortpath, split_args
if typing.TYPE_CHECKING:
from .backend.backends import TestSerialisation
@ -88,7 +87,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
help='Run test under gdb.')
parser.add_argument('--list', default=False, dest='list', action='store_true',
help='List available tests.')
parser.add_argument('--wrapper', default=None, dest='wrapper', type=shlex.split,
parser.add_argument('--wrapper', default=None, dest='wrapper', type=split_args,
help='wrapper to run tests with (e.g. Valgrind)')
parser.add_argument('-C', default='.', dest='wd',
help='directory to cd into before running')
@ -116,7 +115,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
' more time to execute.')
parser.add_argument('--setup', default=None, dest='setup',
help='Which test setup to use.')
parser.add_argument('--test-args', default=[], type=shlex.split,
parser.add_argument('--test-args', default=[], type=split_args,
help='Arguments to pass to the specified test(s) or all tests')
parser.add_argument('args', nargs='*',
help='Optional list of tests to run')

@ -14,10 +14,9 @@
import sys, os
import subprocess
import shlex
import shutil
import argparse
from ..mesonlib import MesonException, Popen_safe, is_windows
from ..mesonlib import MesonException, Popen_safe, is_windows, split_args
from . import destdir_join
parser = argparse.ArgumentParser()
@ -149,7 +148,7 @@ def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs,
'--output-dir=' + abs_out]
library_paths = []
for ldflag in shlex.split(ldflags):
for ldflag in split_args(ldflags):
if ldflag.startswith('-Wl,-rpath,'):
library_paths.append(ldflag[11:])

@ -13,12 +13,11 @@
# limitations under the License.
import os
import shlex
import subprocess
import shutil
import tempfile
from ..environment import detect_ninja
from ..mesonlib import Popen_safe
from ..mesonlib import Popen_safe, split_args
def scanbuild(exelist, srcdir, blddir, privdir, logdir, args):
with tempfile.TemporaryDirectory(dir=privdir) as scandir:
@ -63,7 +62,7 @@ def run(args):
break
if 'SCANBUILD' in os.environ:
exelist = shlex.split(os.environ['SCANBUILD'])
exelist = split_args(os.environ['SCANBUILD'])
else:
exelist = [toolname]

@ -114,7 +114,8 @@ def get_relative_files_list_from_dir(fromdir: Path) -> typing.List[Path]:
def platform_fix_name(fname: str, compiler, env) -> str:
# canonicalize compiler
if compiler in {'clang-cl', 'intel-cl'}:
if (compiler in {'clang-cl', 'intel-cl'} or
(env.machines.host.is_windows() and compiler == 'pgi')):
canonical_compiler = 'msvc'
else:
canonical_compiler = compiler

@ -117,7 +117,7 @@ Backend = Enum('Backend', 'ninja vs xcode')
if 'MESON_EXE' in os.environ:
import shlex
meson_exe = shlex.split(os.environ['MESON_EXE'])
meson_exe = mesonlib.split_args(os.environ['MESON_EXE'])
else:
meson_exe = None

@ -14,7 +14,6 @@
# limitations under the License.
import stat
import shlex
import subprocess
import re
import json
@ -51,7 +50,7 @@ from mesonbuild.ast import AstInterpreter
from mesonbuild.mesonlib import (
BuildDirLock, LibType, MachineChoice, PerMachine, Version,
is_windows, is_osx, is_cygwin, is_dragonflybsd, is_openbsd, is_haiku,
windows_proof_rmtree, python_command, version_compare,
windows_proof_rmtree, python_command, version_compare, split_args, quote_arg
)
from mesonbuild.environment import detect_ninja
from mesonbuild.mesonlib import MesonException, EnvironmentException
@ -1022,6 +1021,103 @@ class InternalTests(unittest.TestCase):
self.assertTrue(vctools_ver.startswith(toolset_ver),
msg='{!r} does not start with {!r}'.format(vctools_ver, toolset_ver))
def test_split_args(self):
split_args = mesonbuild.mesonlib.split_args
join_args = mesonbuild.mesonlib.join_args
if is_windows():
test_data = [
# examples from https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments
(r'"a b c" d e', ['a b c', 'd', 'e'], True),
(r'"ab\"c" "\\" d', ['ab"c', '\\', 'd'], False),
(r'a\\\b d"e f"g h', [r'a\\\b', 'de fg', 'h'], False),
(r'a\\\"b c d', [r'a\"b', 'c', 'd'], False),
(r'a\\\\"b c" d e', [r'a\\b c', 'd', 'e'], False),
# other basics
(r'""', [''], True),
(r'a b c d "" e', ['a', 'b', 'c', 'd', '', 'e'], True),
(r"'a b c' d e", ["'a", 'b', "c'", 'd', 'e'], True),
(r"'a&b&c' d e", ["'a&b&c'", 'd', 'e'], True),
(r"a & b & c d e", ['a', '&', 'b', '&', 'c', 'd', 'e'], True),
(r"'a & b & c d e'", ["'a", '&', 'b', '&', 'c', 'd', "e'"], True),
('a b\nc\rd \n\re', ['a', 'b', 'c', 'd', 'e'], False),
# more illustrative tests
(r'cl test.cpp /O1 /Fe:test.exe', ['cl', 'test.cpp', '/O1', '/Fe:test.exe'], True),
(r'cl "test.cpp /O1 /Fe:test.exe"', ['cl', 'test.cpp /O1 /Fe:test.exe'], True),
(r'cl /DNAME=\"Bob\" test.cpp', ['cl', '/DNAME="Bob"', 'test.cpp'], False),
(r'cl "/DNAME=\"Bob\"" test.cpp', ['cl', '/DNAME="Bob"', 'test.cpp'], True),
(r'cl /DNAME=\"Bob, Alice\" test.cpp', ['cl', '/DNAME="Bob,', 'Alice"', 'test.cpp'], False),
(r'cl "/DNAME=\"Bob, Alice\"" test.cpp', ['cl', '/DNAME="Bob, Alice"', 'test.cpp'], True),
(r'cl C:\path\with\backslashes.cpp', ['cl', r'C:\path\with\backslashes.cpp'], True),
(r'cl C:\\path\\with\\double\\backslashes.cpp', ['cl', r'C:\\path\\with\\double\\backslashes.cpp'], True),
(r'cl "C:\\path\\with\\double\\backslashes.cpp"', ['cl', r'C:\\path\\with\\double\\backslashes.cpp'], False),
(r'cl C:\path with spaces\test.cpp', ['cl', r'C:\path', 'with', r'spaces\test.cpp'], False),
(r'cl "C:\path with spaces\test.cpp"', ['cl', r'C:\path with spaces\test.cpp'], True),
(r'cl /DPATH="C:\path\with\backslashes test.cpp', ['cl', r'/DPATH=C:\path\with\backslashes test.cpp'], False),
(r'cl /DPATH=\"C:\\ends\\with\\backslashes\\\" test.cpp', ['cl', r'/DPATH="C:\\ends\\with\\backslashes\"', 'test.cpp'], False),
(r'cl /DPATH="C:\\ends\\with\\backslashes\\" test.cpp', ['cl', '/DPATH=C:\\\\ends\\\\with\\\\backslashes\\', 'test.cpp'], False),
(r'cl "/DNAME=\"C:\\ends\\with\\backslashes\\\"" test.cpp', ['cl', r'/DNAME="C:\\ends\\with\\backslashes\"', 'test.cpp'], True),
(r'cl "/DNAME=\"C:\\ends\\with\\backslashes\\\\"" test.cpp', ['cl', r'/DNAME="C:\\ends\\with\\backslashes\\ test.cpp'], False),
(r'cl "/DNAME=\"C:\\ends\\with\\backslashes\\\\\"" test.cpp', ['cl', r'/DNAME="C:\\ends\\with\\backslashes\\"', 'test.cpp'], True),
]
else:
test_data = [
(r"'a b c' d e", ['a b c', 'd', 'e'], True),
(r"a/b/c d e", ['a/b/c', 'd', 'e'], True),
(r"a\b\c d e", [r'abc', 'd', 'e'], False),
(r"a\\b\\c d e", [r'a\b\c', 'd', 'e'], False),
(r'"a b c" d e', ['a b c', 'd', 'e'], False),
(r'"a\\b\\c\\" d e', ['a\\b\\c\\', 'd', 'e'], False),
(r"'a\b\c\' d e", ['a\\b\\c\\', 'd', 'e'], True),
(r"'a&b&c' d e", ['a&b&c', 'd', 'e'], True),
(r"a & b & c d e", ['a', '&', 'b', '&', 'c', 'd', 'e'], False),
(r"'a & b & c d e'", ['a & b & c d e'], True),
(r"abd'e f'g h", [r'abde fg', 'h'], False),
('a b\nc\rd \n\re', ['a', 'b', 'c', 'd', 'e'], False),
('g++ -DNAME="Bob" test.cpp', ['g++', '-DNAME=Bob', 'test.cpp'], False),
("g++ '-DNAME=\"Bob\"' test.cpp", ['g++', '-DNAME="Bob"', 'test.cpp'], True),
('g++ -DNAME="Bob, Alice" test.cpp', ['g++', '-DNAME=Bob, Alice', 'test.cpp'], False),
("g++ '-DNAME=\"Bob, Alice\"' test.cpp", ['g++', '-DNAME="Bob, Alice"', 'test.cpp'], True),
]
for (cmd, expected, roundtrip) in test_data:
self.assertEqual(split_args(cmd), expected)
if roundtrip:
self.assertEqual(join_args(expected), cmd)
def test_quote_arg(self):
split_args = mesonbuild.mesonlib.split_args
quote_arg = mesonbuild.mesonlib.quote_arg
if is_windows():
test_data = [
('', '""'),
('arg1', 'arg1'),
('/option1', '/option1'),
('/Ovalue', '/Ovalue'),
('/OBob&Alice', '/OBob&Alice'),
('/Ovalue with spaces', r'"/Ovalue with spaces"'),
(r'/O"value with spaces"', r'"/O\"value with spaces\""'),
(r'/OC:\path with spaces\test.exe', r'"/OC:\path with spaces\test.exe"'),
('/LIBPATH:C:\\path with spaces\\ends\\with\\backslashes\\', r'"/LIBPATH:C:\path with spaces\ends\with\backslashes\\"'),
('/LIBPATH:"C:\\path with spaces\\ends\\with\\backslashes\\\\"', r'"/LIBPATH:\"C:\path with spaces\ends\with\backslashes\\\\\""'),
(r'/DMSG="Alice said: \"Let\'s go\""', r'"/DMSG=\"Alice said: \\\"Let\'s go\\\"\""'),
]
else:
test_data = [
('arg1', 'arg1'),
('--option1', '--option1'),
('-O=value', '-O=value'),
('-O=Bob&Alice', "'-O=Bob&Alice'"),
('-O=value with spaces', "'-O=value with spaces'"),
('-O="value with spaces"', '\'-O=\"value with spaces\"\''),
('-O=/path with spaces/test', '\'-O=/path with spaces/test\''),
('-DMSG="Alice said: \\"Let\'s go\\""', "'-DMSG=\"Alice said: \\\"Let'\"'\"'s go\\\"\"'"),
]
for (arg, expected) in test_data:
self.assertEqual(quote_arg(arg), expected)
self.assertEqual(split_args(expected)[0], arg)
@unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release')
class DataTests(unittest.TestCase):
@ -2033,7 +2129,7 @@ class AllPlatformTests(BasePlatformTests):
if not execmd or not fxecmd:
raise Exception('Could not find someexe and somfxe commands')
# Check include order for 'someexe'
incs = [a for a in shlex.split(execmd) if a.startswith("-I")]
incs = [a for a in split_args(execmd) if a.startswith("-I")]
self.assertEqual(len(incs), 9)
# target private dir
someexe_id = Target.construct_id_from_path("sub4", "someexe", "@exe")
@ -2055,7 +2151,7 @@ class AllPlatformTests(BasePlatformTests):
# custom target include dir
self.assertPathEqual(incs[8], '-Ictsub')
# Check include order for 'somefxe'
incs = [a for a in shlex.split(fxecmd) if a.startswith('-I')]
incs = [a for a in split_args(fxecmd) if a.startswith('-I')]
self.assertEqual(len(incs), 9)
# target private dir
self.assertPathEqual(incs[0], '-Isomefxe@exe')
@ -2123,7 +2219,7 @@ class AllPlatformTests(BasePlatformTests):
else:
raise AssertionError('Unknown compiler {!r}'.format(evalue))
# Check that we actually used the evalue correctly as the compiler
self.assertEqual(ecc.get_exelist(), shlex.split(evalue))
self.assertEqual(ecc.get_exelist(), split_args(evalue))
# Do auto-detection of compiler based on platform, PATH, etc.
cc = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
self.assertTrue(cc.version)
@ -2174,14 +2270,14 @@ class AllPlatformTests(BasePlatformTests):
wrappercc = python_command + [wrapper] + cc.get_exelist() + ['-DSOME_ARG']
wrappercc_s = ''
for w in wrappercc:
wrappercc_s += shlex.quote(w) + ' '
wrappercc_s += quote_arg(w) + ' '
os.environ[evar] = wrappercc_s
wcc = getattr(env, 'detect_{}_compiler'.format(lang))(MachineChoice.HOST)
# Check static linker too
wrapperlinker = python_command + [wrapper] + linker.get_exelist() + linker.get_always_args()
wrapperlinker_s = ''
for w in wrapperlinker:
wrapperlinker_s += shlex.quote(w) + ' '
wrapperlinker_s += quote_arg(w) + ' '
os.environ['AR'] = wrapperlinker_s
wlinker = env.detect_static_linker(wcc)
# Pop it so we don't use it for the next detection
@ -2207,7 +2303,7 @@ class AllPlatformTests(BasePlatformTests):
commands = {'c-asm': {}, 'cpp-asm': {}, 'cpp-c-asm': {}, 'c-cpp-asm': {}}
for cmd in self.get_compdb():
# Get compiler
split = shlex.split(cmd['command'])
split = split_args(cmd['command'])
if split[0] == 'ccache':
compiler = split[1]
else:
@ -2272,7 +2368,7 @@ class AllPlatformTests(BasePlatformTests):
define = 'MESON_TEST_DEFINE_VALUE'
# NOTE: this list can't have \n, ' or "
# \n is never substituted by the GNU pre-processor via a -D define
# ' and " confuse shlex.split() even when they are escaped
# ' and " confuse split_args() even when they are escaped
# % and # confuse the MSVC preprocessor
# !, ^, *, and < confuse lcc preprocessor
value = 'spaces and fun@$&()-=_+{}[]:;>?,./~`'
@ -3154,7 +3250,7 @@ recommended as it is not supported on some platforms''')
self.assertEqual(obj.user_options['subp:subp_opt'].value, 'foo')
self.wipe()
# c_args value should be parsed with shlex
# c_args value should be parsed with split_args
self.init(testdir, extra_args=['-Dc_args=-Dfoo -Dbar "-Dthird=one two"'])
obj = mesonbuild.coredata.load(self.builddir)
self.assertEqual(obj.compiler_options.host['c_args'].value, ['-Dfoo', '-Dbar', '-Dthird=one two'])

Loading…
Cancel
Save