cmake: moved subprojects into the CMake module

pull/4969/head
Daniel Mensinger 6 years ago
parent af6448ced5
commit e02c101575
No known key found for this signature in database
GPG Key ID: 54DD94C131E277D4
  1. 18
      mesonbuild/cmake/interpreter.py
  2. 30
      mesonbuild/interpreter.py
  3. 70
      mesonbuild/modules/cmake.py
  4. 2
      mesonbuild/msubprojects.py
  5. 15
      mesonbuild/wrap/wrap.py

@ -25,7 +25,7 @@ from ..backend.backends import Backend
from ..compilers.compilers import obj_suffixes
from ..dependencies.base import CMakeDependency, ExternalProgram
from subprocess import Popen, PIPE, STDOUT
from typing import List
from typing import List, Dict, Optional
import os, re
backend_generator_map = {
@ -284,6 +284,9 @@ class CMakeInterpreter:
self.languages = []
self.targets = []
# Generated meson data
self.generated_targets = {}
def configure(self, extra_cmake_options: List[str]) -> None:
# Find CMake
cmake_exe, cmake_vers, _ = CMakeDependency.find_cmake_binary(self.env)
@ -297,7 +300,7 @@ class CMakeInterpreter:
cmake_args = cmake_exe.get_command()
# Map meson compiler to CMake variables
for lang, comp in self.build.compilers.items():
for lang, comp in self.env.coredata.compilers.items():
if lang not in language_map:
continue
cmake_lang = language_map[lang]
@ -530,11 +533,20 @@ class CMakeInterpreter:
# Add the nodes to the ast
root_cb.lines += [inc_node, src_node, tgt_node, dep_node]
processed[tgt.name] = {'inc': inc_var, 'src': src_var, 'dep': dep_var, 'tgt': tgt_var}
processed[tgt.name] = {'inc': inc_var, 'src': src_var, 'dep': dep_var, 'tgt': tgt_var, 'func': tgt_func}
# Now generate the target function calls
for i in self.targets:
if i.name not in processed:
process_target(i)
self.generated_targets = processed
return root_cb
def target_info(self, target: str) -> Optional[Dict[str, str]]:
if target in self.generated_targets:
return self.generated_targets[target]
return None
def target_list(self) -> List[str]:
return list(self.generated_targets.keys())

@ -2247,7 +2247,7 @@ class Interpreter(InterpreterBase):
raise InterpreterException('Stdlib definition for %s should have exactly two elements.'
% l)
projname, depname = di
subproj = self.do_subproject(projname, {})
subproj = self.do_subproject(projname, 'meson', {})
self.build.cross_stdlibs[l] = subproj.get_variable_method([depname], {})
except KeyError:
pass
@ -2416,20 +2416,19 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.')
@FeatureNewKwargs('subproject', '0.38.0', ['default_options'])
@FeatureNewKwargs('subproject', '0.51.0', ['method', 'cmake_options'])
@permittedKwargs(permitted_kwargs['subproject'])
@stringArgs
def func_subproject(self, nodes, args, kwargs):
if len(args) != 1:
raise InterpreterException('Subproject takes exactly one argument')
dirname = args[0]
return self.do_subproject(dirname, kwargs)
return self.do_subproject(dirname, 'meson', kwargs)
def disabled_subproject(self, dirname):
self.subprojects[dirname] = SubprojectHolder(None, self.subproject_dir, dirname)
return self.subprojects[dirname]
def do_subproject(self, dirname, kwargs):
def do_subproject(self, dirname: str, method: str, kwargs):
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Subproject', mlog.bold(dirname), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
@ -2437,7 +2436,6 @@ external dependencies (including libraries) must go to "dependencies".''')
default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
default_options = coredata.create_options_dict(default_options)
method = kwargs.get('method', 'auto')
if dirname == '':
raise InterpreterException('Subproject dir name must not be empty.')
if dirname[0] == '.':
@ -2463,7 +2461,7 @@ external dependencies (including libraries) must go to "dependencies".''')
subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
r = wrap.Resolver(subproject_dir_abs, self.coredata.get_builtin_option('wrap_mode'))
try:
resolved = r.resolve(dirname)
resolved = r.resolve(dirname, method)
except wrap.WrapException as e:
subprojdir = os.path.join(self.subproject_dir, r.directory)
if isinstance(e, wrap.WrapNotFoundException):
@ -2483,21 +2481,14 @@ external dependencies (including libraries) must go to "dependencies".''')
os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
self.global_args_frozen = True
# Determine the method to use when 'auto' is given
if method == 'auto':
if os.path.exists(os.path.join(subdir_abs, environment.build_filename)):
method = 'meson'
elif os.path.exists(os.path.join(subdir_abs, 'CMakeLists.txt')):
method = 'cmake'
mlog.log()
with mlog.nested():
mlog.log('Executing subproject', mlog.bold(dirname), 'method', mlog.bold(method), '\n')
try:
if method == 'meson':
return self.do_subproject_meson(dirname, subdir, default_options, required, kwargs)
return self._do_subproject_meson(dirname, subdir, default_options, required, kwargs)
elif method == 'cmake':
return self.do_subproject_cmake(dirname, subdir, subdir_abs, default_options, required, kwargs)
return self._do_subproject_cmake(dirname, subdir, subdir_abs, default_options, required, kwargs)
else:
raise InterpreterException('The method {} is invalid for the subproject {}'.format(method, dirname))
# Invalid code is always an error
@ -2513,7 +2504,7 @@ external dependencies (including libraries) must go to "dependencies".''')
return self.disabled_subproject(dirname)
raise e
def do_subproject_meson(self, dirname, subdir, default_options, required, kwargs, ast=None, build_def_files=None):
def _do_subproject_meson(self, dirname, subdir, default_options, required, kwargs, ast=None, build_def_files=None):
with mlog.nested():
new_build = self.build.copy()
subi = Interpreter(new_build, self.backend, dirname, subdir, self.subproject_dir,
@ -2544,7 +2535,7 @@ external dependencies (including libraries) must go to "dependencies".''')
self.build.subprojects[dirname] = subi.project_version
return self.subprojects[dirname]
def do_subproject_cmake(self, dirname, subdir, subdir_abs, default_options, required, kwargs):
def _do_subproject_cmake(self, dirname, subdir, subdir_abs, default_options, required, kwargs):
with mlog.nested():
new_build = self.build.copy()
prefix = self.coredata.builtins['prefix'].value
@ -2572,7 +2563,8 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.debug('=== END meson.build ===')
mlog.debug()
result = self.do_subproject_meson(dirname, subdir, default_options, required, kwargs, ast, cm_int.bs_files)
result = self._do_subproject_meson(dirname, subdir, default_options, required, kwargs, ast, cm_int.bs_files)
result.cm_interpreter = cm_int
mlog.log()
return result
@ -3164,7 +3156,7 @@ external dependencies (including libraries) must go to "dependencies".''')
'default_options': kwargs.get('default_options', []),
'required': kwargs.get('required', True),
}
self.do_subproject(dirname, sp_kwargs)
self.do_subproject(dirname, 'meson', sp_kwargs)
return self.get_subproject_dep(display_name, dirname, varname, kwargs)
@FeatureNewKwargs('executable', '0.42.0', ['implib'])

@ -18,8 +18,8 @@ import shutil
from . import ExtensionModule, ModuleReturnValue
from .. import build, dependencies, mesonlib, mlog
from ..interpreterbase import permittedKwargs
from ..interpreter import ConfigurationDataHolder
from ..interpreterbase import permittedKwargs, FeatureNew, stringArgs, InterpreterObject, ObjectHolder
from ..interpreter import ConfigurationDataHolder, InterpreterException, SubprojectHolder
COMPATIBILITIES = ['AnyNewerVersion', 'SameMajorVersion', 'SameMinorVersion', 'ExactVersion']
@ -43,6 +43,62 @@ unset(_realOrig)
unset(_realCurr)
'''
class CMakeSubprojectHolder(InterpreterObject, ObjectHolder):
def __init__(self, subp, pv):
assert(isinstance(subp, SubprojectHolder))
assert(hasattr(subp, 'cm_interpreter'))
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, subp, pv)
self.methods.update({'get_variable': self.get_variable,
'dependency': self.dependency,
'include_directories': self.include_directories,
'target': self.target,
'target_type': self.target_type,
'target_list': self.target_list,
})
def _args_to_info(self, args):
if len(args) != 1:
raise InterpreterException('Exactly one argument is required.')
tgt = args[0]
res = self.held_object.cm_interpreter.target_info(tgt)
if res is None:
raise InterpreterException('The CMake target {} does not exist'.format(tgt))
# Make sure that all keys are present (if not this is a bug)
assert(all([x in res for x in ['inc', 'src', 'dep', 'tgt', 'func']]))
return res
@permittedKwargs({})
def get_variable(self, args, kwargs):
return self.held_object.get_variable_method(args, kwargs)
@permittedKwargs({})
def dependency(self, args, kwargs):
info = self._args_to_info(args)
return self.get_variable([info['dep']], kwargs)
@permittedKwargs({})
def include_directories(self, args, kwargs):
info = self._args_to_info(args)
return self.get_variable([info['inc']], kwargs)
@permittedKwargs({})
def target(self, args, kwargs):
info = self._args_to_info(args)
return self.get_variable([info['tgt']], kwargs)
@permittedKwargs({})
def target_type(self, args, kwargs):
info = self._args_to_info(args)
return info['func']
@permittedKwargs({})
def target_list(self, args, kwargs):
if len(args) > 0:
raise InterpreterException('target_list does not take any parameters.')
return self.held_object.cm_interpreter.target_list()
class CmakeModule(ExtensionModule):
cmake_detected = False
@ -51,6 +107,7 @@ class CmakeModule(ExtensionModule):
def __init__(self, interpreter):
super().__init__(interpreter)
self.snippets.add('configure_package_config_file')
self.snippets.add('subproject')
def detect_voidp_size(self, env):
compilers = env.coredata.compilers
@ -210,5 +267,14 @@ class CmakeModule(ExtensionModule):
return res
@FeatureNew('subproject', '0.51.0')
@permittedKwargs({'cmake_options'})
@stringArgs
def subproject(self, interpreter, state, args, kwargs):
if len(args) != 1:
raise InterpreterException('Subproject takes exactly one argument')
dirname = args[0]
return CMakeSubprojectHolder(interpreter.do_subproject(dirname, 'cmake', kwargs), dirname)
def initialize(*args, **kwargs):
return CmakeModule(*args, **kwargs)

@ -163,7 +163,7 @@ def download(wrap, repo_dir, options):
return
try:
r = Resolver(os.path.dirname(repo_dir))
r.resolve(wrap.name)
r.resolve(wrap.name, 'meson')
mlog.log(' -> done')
except WrapException as e:
mlog.log(' ->', mlog.red(str(e)))

@ -111,7 +111,7 @@ class Resolver:
self.subdir_root = subdir_root
self.cachedir = os.path.join(self.subdir_root, 'packagecache')
def resolve(self, packagename):
def resolve(self, packagename: str, method: str):
self.packagename = packagename
self.directory = packagename
# We always have to load the wrap file, if it exists, because it could
@ -125,8 +125,13 @@ class Resolver:
meson_file = os.path.join(self.dirname, 'meson.build')
cmake_file = os.path.join(self.dirname, 'CMakeLists.txt')
if method not in ['meson', 'cmake']:
raise WrapException('Only the methods "meson" and "cmake" are supported')
# The directory is there and has meson.build? Great, use it.
if os.path.exists(meson_file) or os.path.exists(cmake_file):
if method == 'meson' and os.path.exists(meson_file):
return self.directory
if method == 'cmake' and os.path.exists(cmake_file):
return self.directory
# Check if the subproject is a git submodule
@ -154,9 +159,11 @@ class Resolver:
else:
raise WrapException('Unknown wrap type {!r}'.format(self.wrap.type))
# A meson.build file is required in the directory
if not os.path.exists(meson_file):
# A meson.build or CMakeLists.txt file is required in the directory
if method == 'meson' and not os.path.exists(meson_file):
raise WrapException('Subproject exists but has no meson.build file')
if method == 'cmake' and not os.path.exists(cmake_file):
raise WrapException('Subproject exists but has no CMakeLists.txt file')
return self.directory

Loading…
Cancel
Save