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.
This commit is contained in:
parent
9d0ad66c29
commit
93c3ec7e2d
|
@ -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…
Reference in New Issue