Add a `required` argument to `subproject`

Allowing to use the new "feature" option type and allowing not to fail
on subproject if it is not necessary to fail.

By default subprojects are "required" so previous behaviour is not
changed.

Fixes #3880
pull/4092/head
Thibault Saunier 7 years ago committed by Jussi Pakkanen
parent 899b0aae9f
commit 731906504e
  1. 85
      mesonbuild/interpreter.py
  2. 10
      run_unittests.py
  3. 17
      test cases/common/206 subproject with features/meson.build
  4. 3
      test cases/common/206 subproject with features/meson_options.txt
  5. 4
      test cases/common/206 subproject with features/nothing.c
  6. 3
      test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build
  7. 3
      test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build
  8. 5
      test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c
  9. 6
      test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h
  10. 3
      test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build
  11. 2
      test cases/common/206 subproject with features/subprojects/sub/lib/meson.build
  12. 5
      test cases/common/206 subproject with features/subprojects/sub/lib/sub.c
  13. 6
      test cases/common/206 subproject with features/subprojects/sub/lib/sub.h
  14. 3
      test cases/common/206 subproject with features/subprojects/sub/meson.build

@ -383,6 +383,9 @@ class DependencyHolder(InterpreterObject, ObjectHolder):
'partial_dependency': self.partial_dependency_method,
})
def found(self):
return self.found_method([], {})
@noPosargs
@permittedKwargs({})
def type_name_method(self, args, kwargs):
@ -916,16 +919,30 @@ class Test(InterpreterObject):
class SubprojectHolder(InterpreterObject, ObjectHolder):
def __init__(self, subinterpreter):
def __init__(self, subinterpreter, subproject_dir, name):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, subinterpreter)
self.name = name
self.subproject_dir = subproject_dir
self.methods.update({'get_variable': self.get_variable_method,
'found': self.found_method,
})
@noPosargs
@permittedKwargs({})
def found_method(self, args, kwargs):
return self.found()
def found(self):
return self.held_object is not None
@permittedKwargs({})
def get_variable_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('Get_variable takes one argument.')
if not self.found():
raise InterpreterException('Subproject "%s/%s" disabled can\'t get_variable on it.' % (
self.subproject_dir, self.name))
varname = args[0]
if not isinstance(varname, str):
raise InterpreterException('Get_variable takes a string argument.')
@ -1838,7 +1855,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'both_libraries': known_library_kwargs,
'library': known_library_kwargs,
'subdir': {'if_found'},
'subproject': {'version', 'default_options'},
'subproject': {'version', 'default_options', 'required'},
'test': {'args', 'depends', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'},
'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'},
}
@ -2219,7 +2236,16 @@ external dependencies (including libraries) must go to "dependencies".''')
dirname = args[0]
return self.do_subproject(dirname, 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):
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('\nSubproject', mlog.bold(dirname), ':', 'skipped: feature', mlog.bold(feature), 'disabled')
return self.disabled_subproject(dirname)
default_options = mesonlib.stringlistify(kwargs.get('default_options', []))
default_options = coredata.create_options_dict(default_options)
if dirname == '':
@ -2237,7 +2263,13 @@ external dependencies (including libraries) must go to "dependencies".''')
incpath = ' => '.join(fullstack)
raise InvalidCode('Recursive include of subprojects: %s.' % incpath)
if dirname in self.subprojects:
return self.subprojects[dirname]
subproject = self.subprojects[dirname]
if required and not subproject.found():
raise InterpreterException('Subproject "%s/%s" required but not found.' % (
self.subproject_dir, dirname))
return subproject
subproject_dir_abs = os.path.join(self.environment.get_source_dir(), self.subproject_dir)
r = wrap.Resolver(subproject_dir_abs, self.coredata.wrap_mode)
try:
@ -2249,22 +2281,35 @@ external dependencies (including libraries) must go to "dependencies".''')
# promotion...
self.print_nested_info(dirname)
msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}'
raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e))
if required:
msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}'
raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e))
mlog.log('\nSubproject ', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)\n')
return self.disabled_subproject(dirname)
subdir = os.path.join(self.subproject_dir, resolved)
os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True)
self.global_args_frozen = True
mlog.log()
with mlog.nested():
mlog.log('\nExecuting subproject', mlog.bold(dirname), '\n')
subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir,
self.modules, default_options)
subi.subprojects = self.subprojects
subi.subproject_stack = self.subproject_stack + [dirname]
current_active = self.active_projectname
subi.run()
mlog.log('\nSubproject', mlog.bold(dirname), 'finished.')
try:
mlog.log('\nExecuting subproject', mlog.bold(dirname), '\n')
subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir,
self.modules, default_options)
subi.subprojects = self.subprojects
subi.subproject_stack = self.subproject_stack + [dirname]
current_active = self.active_projectname
subi.run()
mlog.log('\nSubproject', mlog.bold(dirname), 'finished.')
except Exception as e:
if not required:
mlog.log(e)
mlog.log('\nSubproject', mlog.bold(dirname), 'is buildable:', mlog.red('NO'), '(disabling)')
return self.disabled_subproject(dirname)
else:
raise e
if 'version' in kwargs:
pv = subi.project_version
@ -2274,7 +2319,7 @@ external dependencies (including libraries) must go to "dependencies".''')
self.active_projectname = current_active
self.build.subprojects[dirname] = subi.project_version
self.subprojects.update(subi.subprojects)
self.subprojects[dirname] = SubprojectHolder(subi)
self.subprojects[dirname] = SubprojectHolder(subi, self.subproject_dir, dirname)
self.build_def_files += subi.build_def_files
return self.subprojects[dirname]
@ -2799,6 +2844,13 @@ external dependencies (including libraries) must go to "dependencies".''')
def get_subproject_dep(self, name, dirname, varname, required):
try:
subproject = self.subprojects[dirname]
if not subproject.found():
if not required:
return DependencyHolder(NotFoundDependency(self.environment), self.subproject)
raise DependencyException('Subproject %s was not found.' % (name))
dep = self.subprojects[dirname].get_variable_method([varname], {})
except InvalidArguments as e:
if required:
@ -2820,6 +2872,9 @@ external dependencies (including libraries) must go to "dependencies".''')
dep = self.get_subproject_dep(name, dirname, varname, required)
if not dep:
return False
if not dep.found():
return dep
found = dep.version_method([], {})
# Don't do a version check if the dependency is not found and not required
if not dep.found_method([], {}) and not required:

@ -2848,6 +2848,16 @@ class FailureTests(BasePlatformTests):
msg = '.*WARNING:.*feature.*build_always_stale.*custom_target.*'
self.assertMesonDoesNotOutput(vcs_tag, msg, meson_version='>=0.43')
def test_missing_subproject_not_required_and_required(self):
self.assertMesonRaises("sub1 = subproject('not-found-subproject', required: false)\n" +
"sub2 = subproject('not-found-subproject', required: true)",
""".*Subproject "subprojects/not-found-subproject" required but not found.*""")
def test_get_variable_on_not_found_project(self):
self.assertMesonRaises("sub1 = subproject('not-found-subproject', required: false)\n" +
"sub1.get_variable('naaa')",
"""Subproject "subprojects/not-found-subproject" disabled can't get_variable on it.""")
class WindowsTests(BasePlatformTests):
'''

@ -0,0 +1,17 @@
project('proj', 'c')
auto_subproj = subproject('sub', required: get_option('use-subproject'))
assert(auto_subproj.found(), 'Subproject should always be buildable and thus found')
auto_dep = dependency('', fallback: ['sub', 'libSub'], required: true)
assert(auto_dep.found() == true, 'Subproject is required and foundable, dependency should be found.')
disabled_subproj = subproject('disabled_sub', required: get_option('disabled-subproject'))
assert(disabled_subproj.found() == false, 'Disabled subproject should be NOT found')
disabled_dep = dependency('', fallback: ['disabled_sub', 'libSub'], required: false)
assert(disabled_dep.found() == false, 'Subprojetc was disabled, it should never be built.')
nothing = executable('nothing', 'nothing.c', dependencies: [disabled_dep])
subproj_with_missing_dep = subproject('auto_sub_with_missing_dep', required: get_option('auto-sub-with-missing-dep'))
assert(subproj_with_missing_dep.found() == false, 'Subproject with required=auto and missing dependency should be NOT found')

@ -0,0 +1,3 @@
option('use-subproject', type : 'feature', value : 'auto')
option('disabled-subproject', type : 'feature', value : 'disabled')
option('auto-sub-with-missing-dep', type : 'feature', value : 'auto')

@ -0,0 +1,4 @@
int main(int argc, char const *argv[])
{
return 0;
}

@ -0,0 +1,3 @@
project('sub', 'c')
dependency('no_way_this_exists', required: true)

@ -0,0 +1,3 @@
lib = static_library('sub', 'sub.c')
libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib)

@ -0,0 +1,2 @@
lib = static_library('sub', 'sub.c')
libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib)

@ -0,0 +1,6 @@
#ifndef SUB_H
#define SUB_H
int sub();
#endif
Loading…
Cancel
Save