compilers: Return CompilerArgs from compiler instance

Since the CompileArgs class already needs to know about the compiler,
and we really need at least per-lanaguage if not per-compiler
CompilerArgs classes, let's get the CompilerArgs instance from the
compiler using a method.
pull/7369/head
Dylan Baker 5 years ago
parent 9d0ad66c29
commit 93c3ec7e2d
  1. 43
      mesonbuild/arglist.py
  2. 3
      mesonbuild/backend/backends.py
  3. 10
      mesonbuild/backend/ninjabackend.py
  4. 9
      mesonbuild/backend/vs2010backend.py
  5. 9
      mesonbuild/compilers/compilers.py
  6. 3
      mesonbuild/compilers/d.py
  7. 18
      mesonbuild/compilers/mixins/clike.py
  8. 4
      mesonbuild/linkers.py
  9. 20
      run_unittests.py

@ -21,9 +21,6 @@ import re
import typing as T
from . import mesonlib
from .linkers import (
GnuLikeDynamicLinkerMixin, LinkerEnvVarsMixin, SolarisDynamicLinker,
)
if T.TYPE_CHECKING:
from .linkers import StaticLinker
@ -36,6 +33,7 @@ if mesonlib.is_freebsd() or mesonlib.is_netbsd():
UNIXY_COMPILER_INTERNAL_LIBS.append('execinfo')
SOREGEX = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$')
class Dedup(enum.Enum):
"""What kind of deduplication can be done to compiler args.
@ -88,29 +86,28 @@ class CompilerArgs(collections.abc.MutableSequence):
['-Ifoo', '-Ibar', '-Ifez', '-Ibaz', '-Werror']
'''
# NOTE: currently this class is only for C-like compilers, but it can be
# extended to other languages easily. Just move the following to the
# compiler class and initialize when self.compiler is set.
# Arg prefixes that override by prepending instead of appending
prepend_prefixes = ('-I', '-L')
prepend_prefixes = () # type: T.Tuple[str, ...]
# Arg prefixes and args that must be de-duped by returning 2
dedup2_prefixes = ('-I', '-isystem', '-L', '-D', '-U')
dedup2_suffixes = ()
dedup2_args = ()
dedup2_prefixes = () # type: T.Tuple[str, ...]
dedup2_suffixes = () # type: T.Tuple[str, ...]
dedup2_args = () # type: T.Tuple[str, ...]
# Arg prefixes and args that must be de-duped by returning 1
#
# NOTE: not thorough. A list of potential corner cases can be found in
# https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038
dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic')
dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a')
dedup1_prefixes = () # type: T.Tuple[str, ...]
dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') # type: T.Tuple[str, ...]
# Match a .so of the form path/to/libfoo.so.0.1.0
# Only UNIX shared libraries require this. Others have a fixed extension.
dedup1_regex = re.compile(r'([\/\\]|\A)lib.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$')
dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
dedup1_args = () # type: T.Tuple[str, ...]
# In generate_link() we add external libs without de-dup, but we must
# *always* de-dup these because they're special arguments to the linker
always_dedup_args = tuple('-l' + lib for lib in UNIXY_COMPILER_INTERNAL_LIBS)
# TODO: these should probably move too
always_dedup_args = tuple('-l' + lib for lib in UNIXY_COMPILER_INTERNAL_LIBS) # type : T.Tuple[str, ...]
def __init__(self, compiler: T.Union['Compiler', 'StaticLinker'],
iterable: T.Optional[T.Iterable[str]] = None):
@ -195,7 +192,7 @@ class CompilerArgs(collections.abc.MutableSequence):
def copy(self) -> 'CompilerArgs':
self.flush_pre_post()
return CompilerArgs(self.compiler, self.__container.copy())
return type(self)(self.compiler, self.__container.copy())
@classmethod
@lru_cache(maxsize=None)
@ -218,15 +215,6 @@ class CompilerArgs(collections.abc.MutableSequence):
# both of which are invalid.
if arg in cls.dedup2_prefixes:
return Dedup.NO_DEDUP
if arg.startswith('-L='):
# DMD and LDC proxy all linker arguments using -L=; in conjunction
# with ld64 on macOS this can lead to command line arguments such
# as: `-L=-compatibility_version -L=0 -L=current_version -L=0`.
# These cannot be combined, ld64 insists they must be passed with
# spaces and quoting does not work. if we deduplicate these then
# one of the -L=0 arguments will be removed and the version
# argument will consume the next argument instead.
return Dedup.NO_DEDUP
if arg in cls.dedup2_args or \
arg.startswith(cls.dedup2_prefixes) or \
arg.endswith(cls.dedup2_suffixes):
@ -251,6 +239,7 @@ class CompilerArgs(collections.abc.MutableSequence):
def to_native(self, copy: bool = False) -> T.List[str]:
# XXX: gross
from .compilers import Compiler
from .linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker
# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
@ -376,7 +365,7 @@ class CompilerArgs(collections.abc.MutableSequence):
def __radd__(self, args: T.Iterable[str]) -> 'CompilerArgs':
self.flush_pre_post()
new = CompilerArgs(self.compiler, args)
new = type(self)(self.compiler, args)
new += self
return new
@ -397,4 +386,4 @@ class CompilerArgs(collections.abc.MutableSequence):
def __repr__(self) -> str:
self.flush_pre_post()
return 'CompilerArgs({!r}, {!r})'.format(self.compiler, self.__container)
return '{}({!r}, {!r})'.format(self.__name__, self.compiler, self.__container)

@ -28,7 +28,6 @@ from .. import build
from .. import dependencies
from .. import mesonlib
from .. import mlog
from ..arglist import CompilerArgs
from ..mesonlib import (
File, MachineChoice, MesonException, OrderedSet, OptionOverrideProxy,
classify_unity_sources, unholder
@ -626,7 +625,7 @@ class Backend:
# Create an empty commands list, and start adding arguments from
# various sources in the order in which they must override each other
# starting from hard-coded defaults followed by build options and so on.
commands = CompilerArgs(compiler)
commands = compiler.compiler_args()
copt_proxy = self.get_compiler_options_for_target(target)[compiler.language]
# First, the trivial ones that are impossible to override.

@ -1202,7 +1202,7 @@ int dummy;
compiler = target.compilers['cs']
rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list]
deps = []
commands = CompilerArgs(compiler, target.extra_args.get('cs', []))
commands = compiler.compiler_args(target.extra_args.get('cs', []))
commands += compiler.get_buildtype_args(buildtype)
commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target))
commands += compiler.get_debug_args(self.get_option_for_target('debug', target))
@ -2156,7 +2156,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
def generate_llvm_ir_compile(self, target, src):
compiler = get_compiler_for_source(target.compilers.values(), src)
commands = CompilerArgs(compiler)
commands = compiler.compiler_args()
# Compiler args for compiling this target
commands += compilers.get_base_compile_args(self.environment.coredata.base_options,
compiler)
@ -2245,7 +2245,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
base_proxy = self.get_base_options_for_target(target)
# Create an empty commands list, and start adding arguments from
# various sources in the order in which they must override each other
commands = CompilerArgs(compiler)
commands = compiler.compiler_args()
# Start with symbol visibility.
commands += compiler.gnu_symbol_visibility_args(target.gnu_symbol_visibility)
# Add compiler args for compiling this target derived from 'base' build
@ -2325,7 +2325,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
compiler = get_compiler_for_source(target.compilers.values(), src)
commands = self._generate_single_compile(target, compiler, is_generated)
commands = CompilerArgs(commands.compiler, commands)
commands = commands.compiler.compiler_args(commands)
# Create introspection information
if is_generated is False:
@ -2674,7 +2674,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
#
# Once all the linker options have been passed, we will start passing
# libraries and library paths from internal and external sources.
commands = CompilerArgs(linker)
commands = linker.compiler_args()
# First, the trivial ones that are impossible to override.
#
# Add linker args for linking this target derived from 'base' build

@ -26,7 +26,6 @@ from .. import build
from .. import dependencies
from .. import mlog
from .. import compilers
from ..arglist import CompilerArgs
from ..interpreter import Interpreter
from ..mesonlib import (
MesonException, File, python_command, replace_if_different
@ -899,9 +898,9 @@ class Vs2010Backend(backends.Backend):
#
# file_args is also later split out into defines and include_dirs in
# case someone passed those in there
file_args = dict((lang, CompilerArgs(comp)) for lang, comp in target.compilers.items())
file_defines = dict((lang, []) for lang in target.compilers)
file_inc_dirs = dict((lang, []) for lang in target.compilers)
file_args = {l: c.compiler_args() for l, c in target.compilers.items()}
file_defines = {l: [] for l in target.compilers}
file_inc_dirs = {l: [] for l in target.compilers}
# The order in which these compile args are added must match
# generate_single_compile() and generate_basic_compiler_args()
for l, comp in target.compilers.items():
@ -1084,7 +1083,7 @@ class Vs2010Backend(backends.Backend):
# Linker options
link = ET.SubElement(compiles, 'Link')
extra_link_args = CompilerArgs(compiler)
extra_link_args = compiler.compiler_args()
# FIXME: Can these buildtype linker args be added as tags in the
# vcxproj file (similar to buildtype compiler args) instead of in
# AdditionalOptions?

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import abc
import contextlib, os.path, re, tempfile
import itertools
import typing as T
@ -401,7 +402,7 @@ class RunResult:
self.stderr = stderr
class Compiler:
class Compiler(metaclass=abc.ABCMeta):
# Libraries to ignore in find_library() since they are provided by the
# compiler or the C library. Currently only used for MSVC.
ignore_libs = ()
@ -624,6 +625,10 @@ class Compiler:
args += self.get_preprocess_only_args()
return args
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs:
"""Return an appropriate CompilerArgs instance for this class."""
return CompilerArgs(self, args)
@contextlib.contextmanager
def compile(self, code: str, extra_args: list = None, *, mode: str = 'link', want_output: bool = False, temp_dir: str = None):
if extra_args is None:
@ -642,7 +647,7 @@ class Compiler:
srcname = code.fname
# Construct the compiler command-line
commands = CompilerArgs(self)
commands = self.compiler_args()
commands.append(srcname)
# Preprocess mode outputs to stdout, so no output args
if mode != 'preprocess':

@ -19,7 +19,6 @@ from ..mesonlib import (
EnvironmentException, MachineChoice, version_compare,
)
from ..arglist import CompilerArgs
from .compilers import (
d_dmd_buildtype_args,
d_gdc_buildtype_args,
@ -582,7 +581,7 @@ class DCompiler(Compiler):
elif not isinstance(dependencies, list):
dependencies = [dependencies]
# Collect compiler arguments
args = CompilerArgs(self)
args = self.compiler_args()
for d in dependencies:
# Add compile flags needed by dependencies
args += d.get_compile_args()

@ -32,6 +32,7 @@ from pathlib import Path
from ... import arglist
from ... import mesonlib
from ... import mlog
from ...arglist import CompilerArgs
from ...mesonlib import LibType
from .. import compilers
from .visualstudio import VisualStudioLikeCompiler
@ -40,6 +41,18 @@ if T.TYPE_CHECKING:
from ...environment import Environment
class CLikeCompilerArgs(CompilerArgs):
prepend_prefixes = ('-I', '-L')
dedup2_prefixes = ('-I', '-isystem', '-L', '-D', '-U')
# NOTE: not thorough. A list of potential corner cases can be found in
# https://github.com/mesonbuild/meson/pull/4593#pullrequestreview-182016038
dedup1_prefixes = ('-l', '-Wl,-l', '-Wl,--export-dynamic')
dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a')
dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
class CLikeCompiler:
"""Shared bits for the C and CPP Compilers."""
@ -62,6 +75,9 @@ class CLikeCompiler:
else:
self.exe_wrapper = exe_wrapper.get_command()
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs:
return CLikeCompilerArgs(self, args)
def needs_static_linker(self):
return True # When compiling static libraries, so yes.
@ -339,7 +355,7 @@ class CLikeCompiler:
elif not isinstance(dependencies, list):
dependencies = [dependencies]
# Collect compiler arguments
cargs = arglist.CompilerArgs(self)
cargs = self.compiler_args()
largs = []
for d in dependencies:
# Add compile flags needed by dependencies

@ -17,6 +17,7 @@ import os
import typing as T
from . import mesonlib
from .arglist import CompilerArgs
from .envconfig import get_env_var
if T.TYPE_CHECKING:
@ -29,6 +30,9 @@ class StaticLinker:
def __init__(self, exelist: T.List[str]):
self.exelist = exelist
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArgs:
return CompilerArgs(self, args)
def can_linker_accept_rsp(self) -> bool:
"""
Determines whether the linker can accept arguments using the @rsp syntax.

@ -358,9 +358,8 @@ class InternalTests(unittest.TestCase):
stat.S_IRGRP | stat.S_IXGRP)
def test_compiler_args_class_none_flush(self):
cargsfunc = mesonbuild.arglist.CompilerArgs
cc = mesonbuild.compilers.CCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock())
a = cargsfunc(cc, ['-I.'])
a = cc.compiler_args(['-I.'])
#first we are checking if the tree construction deduplicates the correct -I argument
a += ['-I..']
a += ['-I./tests/']
@ -377,16 +376,15 @@ class InternalTests(unittest.TestCase):
def test_compiler_args_class(self):
cargsfunc = mesonbuild.arglist.CompilerArgs
cc = mesonbuild.compilers.CCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock())
# Test that empty initialization works
a = cargsfunc(cc)
a = cc.compiler_args()
self.assertEqual(a, [])
# Test that list initialization works
a = cargsfunc(cc, ['-I.', '-I..'])
a = cc.compiler_args(['-I.', '-I..'])
self.assertEqual(a, ['-I.', '-I..'])
# Test that there is no de-dup on initialization
self.assertEqual(cargsfunc(cc, ['-I.', '-I.']), ['-I.', '-I.'])
self.assertEqual(cc.compiler_args(['-I.', '-I.']), ['-I.', '-I.'])
## Test that appending works
a.append('-I..')
@ -432,7 +430,7 @@ class InternalTests(unittest.TestCase):
self.assertEqual(a, ['-Ibar', '-Ifoo', '-Ibaz', '-I..', '-I.', '-Ldir', '-Lbah', '-Werror', '-O3', '-O2', '-Wall'])
## Test that adding libraries works
l = cargsfunc(cc, ['-Lfoodir', '-lfoo'])
l = cc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l, ['-Lfoodir', '-lfoo'])
# Adding a library and a libpath appends both correctly
l += ['-Lbardir', '-lbar']
@ -442,7 +440,7 @@ class InternalTests(unittest.TestCase):
self.assertEqual(l, ['-Lbardir', '-Lfoodir', '-lfoo', '-lbar'])
## Test that 'direct' append and extend works
l = cargsfunc(cc, ['-Lfoodir', '-lfoo'])
l = cc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l, ['-Lfoodir', '-lfoo'])
# Direct-adding a library and a libpath appends both correctly
l.extend_direct(['-Lbardir', '-lbar'])
@ -458,14 +456,13 @@ class InternalTests(unittest.TestCase):
self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a'])
def test_compiler_args_class_gnuld(self):
cargsfunc = mesonbuild.arglist.CompilerArgs
## Test --start/end-group
linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', [])
gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
## Test that 'direct' append and extend works
l = cargsfunc(gcc, ['-Lfoodir', '-lfoo'])
l = gcc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Wl,--end-group'])
# Direct-adding a library and a libpath appends both correctly
l.extend_direct(['-Lbardir', '-lbar'])
@ -487,14 +484,13 @@ class InternalTests(unittest.TestCase):
self.assertEqual(l.to_native(copy=True), ['-Lfoo', '-Lfoodir', '-Wl,--start-group', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a', '-Wl,--export-dynamic', '-Wl,-ldl', '-Wl,--end-group'])
def test_compiler_args_remove_system(self):
cargsfunc = mesonbuild.arglist.CompilerArgs
## Test --start/end-group
linker = mesonbuild.linkers.GnuDynamicLinker([], MachineChoice.HOST, 'fake', '-Wl,', [])
gcc = mesonbuild.compilers.GnuCCompiler([], 'fake', False, MachineChoice.HOST, mock.Mock(), linker=linker)
## Ensure that the fake compiler is never called by overriding the relevant function
gcc.get_default_include_dirs = lambda: ['/usr/include', '/usr/share/include', '/usr/local/include']
## Test that 'direct' append and extend works
l = cargsfunc(gcc, ['-Lfoodir', '-lfoo'])
l = gcc.compiler_args(['-Lfoodir', '-lfoo'])
self.assertEqual(l.to_native(copy=True), ['-Lfoodir', '-Wl,--start-group', '-lfoo', '-Wl,--end-group'])
## Test that to_native removes all system includes
l += ['-isystem/usr/include', '-isystem=/usr/share/include', '-DSOMETHING_IMPORTANT=1', '-isystem', '/usr/local/include']

Loading…
Cancel
Save