compilers: Use a distinct type for compile/link results

Currently we do some crazy hackery where we add extra properties to a
Popen object and return that. That's crazy. Especially since some of our
hackery is to delete attributes off of the Popen we don't want. Instead,
let's just have a discrete type that has exactly the properties we want.
pull/7786/head
Dylan Baker 4 years ago
parent 8fa0548e08
commit d326c87fe2
  1. 52
      mesonbuild/compilers/compilers.py
  2. 6
      mesonbuild/compilers/mixins/clike.py
  3. 7
      mesonbuild/compilers/mixins/gnu.py
  4. 2
      mesonbuild/compilers/mixins/visualstudio.py
  5. 4
      mesonbuild/coredata.py

@ -405,6 +405,29 @@ class RunResult:
self.stderr = stderr
class CompileResult:
"""The result of Compiler.compiles (and friends)."""
def __init__(self, stdo: T.Optional[str] = None, stde: T.Optional[str] = None,
args: T.Optional[T.List[str]] = None,
returncode: int = 999, pid: int = -1,
text_mode: bool = True,
input_name: T.Optional[str] = None,
output_name: T.Optional[str] = None,
command: T.Optional[T.List[str]] = None, cached: bool = False):
self.stdout = stdo
self.stderr = stde
self.input_name = input_name
self.output_name = output_name
self.command = command or []
self.args = args or []
self.cached = cached
self.returncode = returncode
self.pid = pid
self.text_mode = text_mode
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.
@ -633,7 +656,7 @@ class Compiler(metaclass=abc.ABCMeta):
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):
def compile(self, code: str, extra_args: list = None, *, mode: str = 'link', want_output: bool = False, temp_dir: str = None) -> T.Iterator[CompileResult]:
if extra_args is None:
extra_args = []
try:
@ -671,15 +694,14 @@ class Compiler(metaclass=abc.ABCMeta):
os_env['LC_ALL'] = 'C'
if no_ccache:
os_env['CCACHE_DISABLE'] = '1'
p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname, env=os_env)
mlog.debug('Compiler stdout:\n', p.stdo)
mlog.debug('Compiler stderr:\n', p.stde)
p.commands = commands
p.input_name = srcname
p, stdo, stde = Popen_safe(commands, cwd=tmpdirname, env=os_env)
mlog.debug('Compiler stdout:\n', stdo)
mlog.debug('Compiler stderr:\n', stde)
result = CompileResult(stdo, stde, list(commands), p.returncode, p.pid, input_name=srcname)
if want_output:
p.output_name = output
p.cached = False # Make sure that the cached attribute always exists
yield p
result.output_name = output
yield result
except OSError:
# On Windows antivirus programs and the like hold on to files so
# they can't be deleted. There's not much to do in this case. Also,
@ -687,7 +709,7 @@ class Compiler(metaclass=abc.ABCMeta):
pass
@contextlib.contextmanager
def cached_compile(self, code, cdata: coredata.CoreData, *, extra_args=None, mode: str = 'link', temp_dir=None):
def cached_compile(self, code, cdata: coredata.CoreData, *, extra_args=None, mode: str = 'link', temp_dir=None) -> T.Iterator[CompileResult]:
assert(isinstance(cdata, coredata.CoreData))
# Calculate the key
@ -697,21 +719,13 @@ class Compiler(metaclass=abc.ABCMeta):
# Check if not cached
if key not in cdata.compiler_check_cache:
with self.compile(code, extra_args=extra_args, mode=mode, want_output=False, temp_dir=temp_dir) as p:
# Remove all attributes except the following
# This way the object can be serialized
tokeep = ['args', 'commands', 'input_name', 'output_name',
'pid', 'returncode', 'stdo', 'stde', 'text_mode']
todel = [x for x in vars(p).keys() if x not in tokeep]
for i in todel:
delattr(p, i)
p.cached = False
cdata.compiler_check_cache[key] = p
yield p
p.cached = True
return
# Return cached
p = cdata.compiler_check_cache[key]
p.cached = True
mlog.debug('Using cached compile:')
mlog.debug('Cached command line: ', ' '.join(p.commands), '\n')
mlog.debug('Code:\n', code)

@ -446,7 +446,7 @@ class CLikeCompiler:
with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
return p.returncode == 0, p.cached
def _build_wrapper(self, code: str, env, extra_args, dependencies=None, mode: str = 'compile', want_output: bool = False, disable_cache: bool = False, temp_dir: str = None) -> T.Tuple[bool, bool]:
def _build_wrapper(self, code: str, env, extra_args, dependencies=None, mode: str = 'compile', want_output: bool = False, disable_cache: bool = False, temp_dir: str = None) -> T.Iterator[compilers.CompileResult]:
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
if disable_cache or want_output:
return self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir)
@ -665,7 +665,7 @@ class CLikeCompiler:
# Get the preprocessed value after the delimiter,
# minus the extra newline at the end and
# merge string literals.
return self.concatenate_string_literals(p.stdo.split(delim + '\n')[-1][:-1]), cached
return self.concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]), cached
def get_return_value(self, fname, rtype, prefix, env, extra_args, dependencies):
if rtype == 'string':
@ -889,7 +889,7 @@ class CLikeCompiler:
with self._build_wrapper(code, env, extra_args=args, mode='compile', want_output=True, temp_dir=env.scratch_dir) as p:
if p.returncode != 0:
m = 'BUG: Unable to compile {!r} check: {}'
raise RuntimeError(m.format(n, p.stdo))
raise RuntimeError(m.format(n, p.stdout))
if not os.path.isfile(p.output_name):
m = 'BUG: Can\'t find compiled test code for {!r} check'
raise RuntimeError(m.format(n))

@ -227,8 +227,7 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
with self._build_wrapper('', env, extra_args=extra_args,
dependencies=None, mode='compile',
want_output=True) as p:
stdo = p.stdo
return stdo
return p.stdout
def _split_fetch_real_dirs(self, pathstr: str) -> T.List[str]:
# We need to use the path separator used by the compiler for printing
@ -364,9 +363,9 @@ class GnuCompiler(GnuLikeCompiler):
# another language, but still complete with exit_success
with self._build_wrapper(code, env, args, None, mode) as p:
result = p.returncode == 0
if self.language in {'cpp', 'objcpp'} and 'is valid for C/ObjC' in p.stde:
if self.language in {'cpp', 'objcpp'} and 'is valid for C/ObjC' in p.stderr:
result = False
if self.language in {'c', 'objc'} and 'is valid for C++/ObjC++' in p.stde:
if self.language in {'c', 'objc'} and 'is valid for C++/ObjC++' in p.stderr:
result = False
return result, p.cached

@ -296,7 +296,7 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
if p.returncode != 0:
return False, p.cached
return not(warning_text in p.stde or warning_text in p.stdo), p.cached
return not(warning_text in p.stderr or warning_text in p.stdout), p.cached
def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
pdbarr = rel_obj.split('.')[:-1]

@ -32,7 +32,7 @@ import typing as T
if T.TYPE_CHECKING:
from . import dependencies
from .compilers import Compiler # noqa: F401
from .compilers import Compiler, CompileResult # noqa: F401
from .environment import Environment
OptionDictType = T.Dict[str, 'UserOption[T.Any]']
@ -394,7 +394,7 @@ class CoreData:
build_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
host_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
self.deps = PerMachine(build_cache, host_cache) # type: PerMachine[DependencyCache]
self.compiler_check_cache = OrderedDict()
self.compiler_check_cache = OrderedDict() # type: T.MutableMapping[T.Tuple[T.Tuple[str, ...], str, str, T.Tuple[str, ...], str], CompileResult]
# Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(options, scratch_dir, 'native')

Loading…
Cancel
Save