From 4dd6cb846900bf827c257f830d54d9fd0932743e Mon Sep 17 00:00:00 2001 From: Daniel Mensinger Date: Sun, 3 Apr 2022 13:06:10 +0200 Subject: [PATCH] cmake: Better error message when configuring a CMake subproject fails. --- mesonbuild/cmake/interpreter.py | 12 ++++++++---- mesonbuild/cmake/traceparser.py | 15 +++++++++++++++ .../122 cmake subproject error/meson.build | 8 ++++++++ .../subprojects/cmlib/CMakeLists.txt | 5 +++++ .../failing/122 cmake subproject error/test.json | 10 ++++++++++ 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 test cases/failing/122 cmake subproject error/meson.build create mode 100644 test cases/failing/122 cmake subproject error/subprojects/cmlib/CMakeLists.txt create mode 100644 test cases/failing/122 cmake subproject error/test.json diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index 3f9326a93..bbb0ce4ba 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -777,7 +777,7 @@ class CMakeInterpreter: # Raw CMake results self.bs_files = [] # type: T.List[Path] self.codemodel_configs = None # type: T.Optional[T.List[CMakeConfiguration]] - self.raw_trace = None # type: T.Optional[str] + self.cmake_stderr = None # type: T.Optional[str] # Analysed data self.project_name = '' @@ -841,13 +841,17 @@ class CMakeInterpreter: final_args = cmake_args + trace_args + cmcmp_args + toolchain.get_cmake_args() + [self.src_dir.as_posix()] cmake_exe.set_exec_mode(print_cmout=True, always_capture_stderr=self.trace.requires_stderr()) - rc, _, self.raw_trace = cmake_exe.call(final_args, self.build_dir, env=os_env, disable_cache=True) + rc, _, self.cmake_stderr = cmake_exe.call(final_args, self.build_dir, env=os_env, disable_cache=True) mlog.log() h = mlog.green('SUCCEEDED') if rc == 0 else mlog.red('FAILED') mlog.log('CMake configuration:', h) if rc != 0: - raise CMakeException('Failed to configure the CMake subproject') + # get the last CMake error - We only need the message function for this: + self.trace.functions = {'message': self.trace.functions['message']} + self.trace.parse(self.cmake_stderr) + error = f': {self.trace.errors[-1]}' if self.trace.errors else '' + raise CMakeException(f'Failed to configure the CMake subproject{error}') return cmake_exe @@ -879,7 +883,7 @@ class CMakeInterpreter: self.custom_targets = [] # Parse the trace - self.trace.parse(self.raw_trace) + self.trace.parse(self.cmake_stderr) # Find all targets added_target_names = [] # type: T.List[str] diff --git a/mesonbuild/cmake/traceparser.py b/mesonbuild/cmake/traceparser.py index 7336d153d..967b40a1b 100644 --- a/mesonbuild/cmake/traceparser.py +++ b/mesonbuild/cmake/traceparser.py @@ -111,6 +111,8 @@ class CMakeTraceParser: self.trace_file_path = build_dir / self.trace_file self.trace_format = 'json-v1' if version_compare(cmake_version, '>=3.17') else 'human' + self.errors: T.List[str] = [] + # State for delayed command execution. Delayed command execution is realised # with a custom CMake file that overrides some functions and adds some # introspection information to the trace. @@ -133,6 +135,7 @@ class CMakeTraceParser: 'target_link_libraries': self._cmake_target_link_libraries, 'target_link_options': self._cmake_target_link_options, 'add_dependencies': self._cmake_add_dependencies, + 'message': self._cmake_message, # Special functions defined in the preload script. # These functions do nothing in the CMake code, but have special @@ -639,6 +642,18 @@ class CMakeTraceParser: # DOC: https://cmake.org/cmake/help/latest/command/target_link_libraries.html self._parse_common_target_options('target_link_options', 'LINK_LIBRARIES', 'INTERFACE_LINK_LIBRARIES', tline) + def _cmake_message(self, tline: CMakeTraceLine) -> None: + # DOC: https://cmake.org/cmake/help/latest/command/message.html + args = list(tline.args) + + if len(args) < 1: + return self._gen_exception('message', 'takes at least 1 argument', tline) + + if args[0].upper().strip() not in ['FATAL_ERROR', 'SEND_ERROR']: + return + + self.errors += [' '.join(args[1:])] + def _parse_common_target_options(self, func: str, private_prop: str, interface_prop: str, tline: CMakeTraceLine, ignore: T.Optional[T.List[str]] = None, paths: bool = False) -> None: if ignore is None: ignore = ['BEFORE'] diff --git a/test cases/failing/122 cmake subproject error/meson.build b/test cases/failing/122 cmake subproject error/meson.build new file mode 100644 index 000000000..d1071bff2 --- /dev/null +++ b/test cases/failing/122 cmake subproject error/meson.build @@ -0,0 +1,8 @@ +project('cmake-executable-dependency', ['c', 'cpp']) + +if not find_program('cmake', required: false).found() + error('MESON_SKIP_TEST CMake is not installed') +endif + +cmake = import('cmake') +cmlib = cmake.subproject('cmlib') diff --git a/test cases/failing/122 cmake subproject error/subprojects/cmlib/CMakeLists.txt b/test cases/failing/122 cmake subproject error/subprojects/cmlib/CMakeLists.txt new file mode 100644 index 000000000..edbe39535 --- /dev/null +++ b/test cases/failing/122 cmake subproject error/subprojects/cmlib/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.5) + +project(cmlib) + +message(FATAL_ERROR "Fancy error message") diff --git a/test cases/failing/122 cmake subproject error/test.json b/test cases/failing/122 cmake subproject error/test.json new file mode 100644 index 000000000..1201da2fb --- /dev/null +++ b/test cases/failing/122 cmake subproject error/test.json @@ -0,0 +1,10 @@ +{ + "stdout": [ + { + "line": "test cases/failing/122 cmake subproject error/meson.build:8:0: ERROR: Failed to configure the CMake subproject: Fancy error message" + } + ], + "tools": { + "cmake": ">=3.14" + } +}