diff options
Diffstat (limited to 'mesonbuild/dependencies/base.py')
-rw-r--r-- | mesonbuild/dependencies/base.py | 342 |
1 files changed, 22 insertions, 320 deletions
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 527b39c..7f2c344 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -32,7 +32,7 @@ from .. import mlog from .. import mesonlib from ..compilers import clib_langs from ..environment import BinaryTable, Environment, MachineInfo -from ..cmake import CMakeExecutor, CMakeException +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 Version, LibType @@ -960,32 +960,6 @@ class PkgConfigDependency(ExternalDependency): return default_value raise DependencyException('Could not get pkg-config variable and no default provided for {!r}'.format(self)) -class CMakeTraceLine: - def __init__(self, file, line, func, args): - self.file = file - self.line = line - self.func = func.lower() - self.args = args - - def __repr__(self): - s = 'CMake TRACE: {0}:{1} {2}({3})' - return s.format(self.file, self.line, self.func, self.args) - -class CMakeTarget: - def __init__(self, name, target_type, properies=None): - if properies is None: - properies = {} - self.name = name - self.type = target_type - self.properies = properies - - def __repr__(self): - s = 'CMake TARGET:\n -- name: {}\n -- type: {}\n -- properies: {{\n{} }}' - propSTR = '' - for i in self.properies: - propSTR += " '{}': {}\n".format(i, self.properies[i]) - return s.format(self.name, self.type, propSTR) - class CMakeDependency(ExternalDependency): # The class's copy of the CMake path. Avoids having to search for it # multiple times in the same Meson invocation. @@ -1025,12 +999,7 @@ class CMakeDependency(ExternalDependency): # stored in the pickled coredata and recovered. self.cmakebin = None self.cmakeinfo = None - - # Dict of CMake variables: '<var_name>': ['list', 'of', 'values'] - self.vars = {} - - # Dict of CMakeTarget - self.targets = {} + self.traceparser = CMakeTraceParser() # Where all CMake "build dirs" are located self.cmake_root_dir = environment.scratch_dir @@ -1110,20 +1079,15 @@ class CMakeDependency(ExternalDependency): return None try: - # First parse the trace - lexer1 = self._lex_trace(err1) - - # Primary pass -- parse all invocations of set - for l in lexer1: - if l.func == 'set': - self._cmake_set(l) + temp_parser = CMakeTraceParser() + temp_parser.parse(err1) except MesonException: return None # Extract the variables and sanity check them - module_paths = sorted(set(self.get_cmake_var('MESON_PATHS_LIST'))) + module_paths = sorted(set(temp_parser.get_cmake_var('MESON_PATHS_LIST'))) module_paths = list(filter(lambda x: os.path.isdir(x), module_paths)) - archs = self.get_cmake_var('MESON_ARCH_LIST') + archs = temp_parser.get_cmake_var('MESON_ARCH_LIST') common_paths = ['lib', 'lib32', 'lib64', 'libx32', 'share'] for i in archs: @@ -1131,7 +1095,7 @@ class CMakeDependency(ExternalDependency): res = { 'module_paths': module_paths, - 'cmake_root': self.get_cmake_var('MESON_CMAKE_ROOT')[0], + 'cmake_root': temp_parser.get_cmake_var('MESON_CMAKE_ROOT')[0], 'archs': archs, 'common_paths': common_paths } @@ -1141,8 +1105,6 @@ class CMakeDependency(ExternalDependency): mlog.debug(' -- CMake architectures: {}'.format(res['archs'])) mlog.debug(' -- CMake lib search paths: {}'.format(res['common_paths'])) - # Reset variables - self.vars = {} return res @staticmethod @@ -1210,7 +1172,6 @@ class CMakeDependency(ExternalDependency): if search_lib_dirs(i): return True - # Check the system paths for i in self.cmakeinfo['module_paths']: if find_module(i): @@ -1283,28 +1244,9 @@ class CMakeDependency(ExternalDependency): return try: - # First parse the trace - lexer1 = self._lex_trace(err1) - - # All supported functions - functions = { - 'set': self._cmake_set, - 'unset': self._cmake_unset, - 'add_executable': self._cmake_add_executable, - 'add_library': self._cmake_add_library, - 'add_custom_target': self._cmake_add_custom_target, - 'set_property': self._cmake_set_property, - 'set_target_properties': self._cmake_set_target_properties - } - - # Primary pass -- parse everything - for l in lexer1: - # "Execute" the CMake function if supported - fn = functions.get(l.func, None) - if(fn): - fn(l) - - except DependencyException as e: + self.traceparser.parse(err1) + except CMakeException as e: + e = self._gen_exception(str(e)) if self.required: raise else: @@ -1315,12 +1257,12 @@ class CMakeDependency(ExternalDependency): return # Whether the package is found or not is always stored in PACKAGE_FOUND - self.is_found = self._var_to_bool('PACKAGE_FOUND') + self.is_found = self.traceparser.var_to_bool('PACKAGE_FOUND') if not self.is_found: return # Try to detect the version - vers_raw = self.get_first_cmake_var_of(['PACKAGE_VERSION']) + vers_raw = self.traceparser.get_cmake_var('PACKAGE_VERSION') if len(vers_raw) > 0: self.version = vers_raw[0] @@ -1333,7 +1275,7 @@ class CMakeDependency(ExternalDependency): # Try guessing a CMake target if none is provided if len(modules) == 0: - for i in self.targets: + for i in self.traceparser.targets: tg = i.lower() lname = name.lower() if '{}::{}'.format(lname, lname) == tg or lname == tg.replace('::', ''): @@ -1344,9 +1286,9 @@ class CMakeDependency(ExternalDependency): # Failed to guess a target --> try the old-style method if len(modules) == 0: - incDirs = self.get_first_cmake_var_of(['PACKAGE_INCLUDE_DIRS']) - defs = self.get_first_cmake_var_of(['PACKAGE_DEFINITIONS']) - libs = self.get_first_cmake_var_of(['PACKAGE_LIBRARIES']) + incDirs = self.traceparser.get_cmake_var('PACKAGE_INCLUDE_DIRS') + defs = self.traceparser.get_cmake_var('PACKAGE_DEFINITIONS') + libs = self.traceparser.get_cmake_var('PACKAGE_LIBRARIES') # Try to use old style variables if no module is specified if len(libs) > 0: @@ -1359,7 +1301,7 @@ class CMakeDependency(ExternalDependency): self.is_found = False raise self._gen_exception('CMake: failed to guess a CMake target for {}.\n' 'Try to explicitly specify one or more targets with the "modules" property.\n' - 'Valid targets are:\n{}'.format(name, list(self.targets.keys()))) + 'Valid targets are:\n{}'.format(name, list(self.traceparser.targets.keys()))) # Set dependencies with CMake targets processed_targets = [] @@ -1368,13 +1310,13 @@ class CMakeDependency(ExternalDependency): compileOptions = [] libraries = [] for i, required in modules: - if i not in self.targets: + if i not in self.traceparser.targets: if not required: mlog.warning('CMake: Optional module', mlog.bold(self._original_module_name(i)), 'for', mlog.bold(name), 'was not found') continue raise self._gen_exception('CMake: invalid module {} for {}.\n' 'Try to explicitly specify one or more targets with the "modules" property.\n' - 'Valid targets are:\n{}'.format(self._original_module_name(i), name, list(self.targets.keys()))) + 'Valid targets are:\n{}'.format(self._original_module_name(i), name, list(self.traceparser.targets.keys()))) targets = [i] if not autodetected_module_list: @@ -1387,7 +1329,7 @@ class CMakeDependency(ExternalDependency): if curr in processed_targets: continue - tgt = self.targets[curr] + tgt = self.traceparser.targets[curr] cfgs = [] cfg = '' otherDeps = [] @@ -1425,7 +1367,7 @@ class CMakeDependency(ExternalDependency): otherDeps += tgt.properies['IMPORTED_LINK_DEPENDENT_LIBRARIES'] for j in otherDeps: - if j in self.targets: + if j in self.traceparser.targets: targets += [j] processed_targets += [curr] @@ -1444,246 +1386,6 @@ class CMakeDependency(ExternalDependency): self.compile_args = compileOptions + compileDefinitions + list(map(lambda x: '-I{}'.format(x), incDirs)) self.link_args = libraries - def get_first_cmake_var_of(self, var_list: List[str]) -> List[str]: - # Return the first found CMake variable in list var_list - for i in var_list: - if i in self.vars: - return self.vars[i] - - return [] - - def get_cmake_var(self, var: str) -> List[str]: - # Return the value of the CMake variable var or an empty list if var does not exist - if var in self.vars: - return self.vars[var] - - return [] - - def _var_to_bool(self, var): - if var not in self.vars: - return False - - if len(self.vars[var]) < 1: - return False - - if self.vars[var][0].upper() in ['1', 'ON', 'TRUE']: - return True - return False - - def _cmake_set(self, tline: CMakeTraceLine) -> None: - """Handler for the CMake set() function in all variaties. - - comes in three flavors: - set(<var> <value> [PARENT_SCOPE]) - set(<var> <value> CACHE <type> <docstring> [FORCE]) - set(ENV{<var>} <value>) - - We don't support the ENV variant, and any uses of it will be ignored - silently. the other two variates are supported, with some caveats: - - we don't properly handle scoping, so calls to set() inside a - function without PARENT_SCOPE set could incorrectly shadow the - outer scope. - - We don't honor the type of CACHE arguments - """ - # DOC: https://cmake.org/cmake/help/latest/command/set.html - - # 1st remove PARENT_SCOPE and CACHE from args - args = [] - for i in tline.args: - if not i or i == 'PARENT_SCOPE': - continue - - # Discard everything after the CACHE keyword - if i == 'CACHE': - break - - args.append(i) - - if len(args) < 1: - raise self._gen_exception('CMake: set() requires at least one argument\n{}'.format(tline)) - - # Now that we've removed extra arguments all that should be left is the - # variable identifier and the value, join the value back together to - # ensure spaces in the value are correctly handled. This assumes that - # variable names don't have spaces. Please don't do that... - identifier = args.pop(0) - value = ' '.join(args) - - if not value: - # Same as unset - if identifier in self.vars: - del self.vars[identifier] - else: - self.vars[identifier] = value.split(';') - - def _cmake_unset(self, tline: CMakeTraceLine): - # DOC: https://cmake.org/cmake/help/latest/command/unset.html - if len(tline.args) < 1: - raise self._gen_exception('CMake: unset() requires at least one argument\n{}'.format(tline)) - - if tline.args[0] in self.vars: - del self.vars[tline.args[0]] - - def _cmake_add_executable(self, tline: CMakeTraceLine): - # DOC: https://cmake.org/cmake/help/latest/command/add_executable.html - args = list(tline.args) # Make a working copy - - # Make sure the exe is imported - if 'IMPORTED' not in args: - raise self._gen_exception('CMake: add_executable() non imported executables are not supported\n{}'.format(tline)) - - args.remove('IMPORTED') - - if len(args) < 1: - raise self._gen_exception('CMake: add_executable() requires at least 1 argument\n{}'.format(tline)) - - self.targets[args[0]] = CMakeTarget(args[0], 'EXECUTABLE', {}) - - def _cmake_add_library(self, tline: CMakeTraceLine): - # DOC: https://cmake.org/cmake/help/latest/command/add_library.html - args = list(tline.args) # Make a working copy - - # Make sure the lib is imported - if 'IMPORTED' not in args: - raise self._gen_exception('CMake: add_library() non imported libraries are not supported\n{}'.format(tline)) - - args.remove('IMPORTED') - - # No only look at the first two arguments (target_name and target_type) and ignore the rest - if len(args) < 2: - raise self._gen_exception('CMake: add_library() requires at least 2 arguments\n{}'.format(tline)) - - self.targets[args[0]] = CMakeTarget(args[0], args[1], {}) - - def _cmake_add_custom_target(self, tline: CMakeTraceLine): - # DOC: https://cmake.org/cmake/help/latest/command/add_custom_target.html - # We only the first parameter (the target name) is interesting - if len(tline.args) < 1: - raise self._gen_exception('CMake: add_custom_target() requires at least one argument\n{}'.format(tline)) - - self.targets[tline.args[0]] = CMakeTarget(tline.args[0], 'CUSTOM', {}) - - def _cmake_set_property(self, tline: CMakeTraceLine) -> None: - # DOC: https://cmake.org/cmake/help/latest/command/set_property.html - args = list(tline.args) - - # We only care for TARGET properties - if args.pop(0) != 'TARGET': - return - - append = False - targets = [] - while args: - curr = args.pop(0) - # XXX: APPEND_STRING is specifically *not* supposed to create a - # list, is treating them as aliases really okay? - if curr == 'APPEND' or curr == 'APPEND_STRING': - append = True - continue - - if curr == 'PROPERTY': - break - - targets.append(curr) - - if not args: - raise self._gen_exception('CMake: set_property() faild to parse argument list\n{}'.format(tline)) - - if len(args) == 1: - # Tries to set property to nothing so nothing has to be done - return - - identifier = args.pop(0) - value = ' '.join(args).split(';') - if not value: - return - - for i in targets: - if i not in self.targets: - raise self._gen_exception('CMake: set_property() TARGET {} not found\n{}'.format(i, tline)) - - if identifier not in self.targets[i].properies: - self.targets[i].properies[identifier] = [] - - if append: - self.targets[i].properies[identifier] += value - else: - self.targets[i].properies[identifier] = value - - def _cmake_set_target_properties(self, tline: CMakeTraceLine) -> None: - # DOC: https://cmake.org/cmake/help/latest/command/set_target_properties.html - args = list(tline.args) - - targets = [] - while args: - curr = args.pop(0) - if curr == 'PROPERTIES': - break - - targets.append(curr) - - # Now we need to try to reconsitute the original quoted format of the - # arguments, as a property value could have spaces in it. Unlike - # set_property() this is not context free. There are two approaches I - # can think of, both have drawbacks: - # - # 1. Assume that the property will be capitalized, this is convention - # but cmake doesn't require it. - # 2. Maintain a copy of the list here: https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties - # - # Neither of these is awesome for obvious reasons. I'm going to try - # option 1 first and fall back to 2, as 1 requires less code and less - # synchroniztion for cmake changes. - - arglist = [] # type: typing.List[typing.Tuple[str, typing.List[str]]] - name = args.pop(0) - values = [] - for a in args: - if a.isupper(): - if values: - arglist.append((name, ' '.join(values).split(';'))) - name = a - values = [] - else: - values.append(a) - if values: - arglist.append((name, ' '.join(values).split(';'))) - - for name, value in arglist: - for i in targets: - if i not in self.targets: - raise self._gen_exception('CMake: set_target_properties() TARGET {} not found\n{}'.format(i, tline)) - - self.targets[i].properies[name] = value - - def _lex_trace(self, trace): - # The trace format is: '<file>(<line>): <func>(<args -- can contain \n> )\n' - reg_tline = re.compile(r'\s*(.*\.(cmake|txt))\(([0-9]+)\):\s*(\w+)\(([\s\S]*?) ?\)\s*\n', re.MULTILINE) - reg_other = re.compile(r'[^\n]*\n') - reg_genexp = re.compile(r'\$<.*>') - loc = 0 - while loc < len(trace): - mo_file_line = reg_tline.match(trace, loc) - if not mo_file_line: - skip_match = reg_other.match(trace, loc) - if not skip_match: - print(trace[loc:]) - raise self._gen_exception('Failed to parse CMake trace') - - loc = skip_match.end() - continue - - loc = mo_file_line.end() - - file = mo_file_line.group(1) - line = mo_file_line.group(3) - func = mo_file_line.group(4) - args = mo_file_line.group(5).split(' ') - args = list(map(lambda x: x.strip(), args)) - args = list(map(lambda x: reg_genexp.sub('', x), args)) # Remove generator expressions - - yield CMakeTraceLine(file, line, func, args) - def _setup_cmake_dir(self, cmake_file: str) -> str: # Setup the CMake build environment and return the "build" directory build_dir = '{}/cmake_{}'.format(self.cmake_root_dir, self.name) @@ -1722,7 +1424,7 @@ class CMakeDependency(ExternalDependency): pkgconfig_define: typing.Optional[typing.List[str]] = None) -> typing.Union[str, typing.List[str]]: if cmake: try: - v = self.vars[cmake] + v = self.traceparser.vars[cmake] except KeyError: pass else: |