diff options
author | Thibault Saunier <tsaunier@igalia.com> | 2018-07-13 13:29:41 -0400 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2018-08-27 21:37:18 +0300 |
commit | 731906504efb57aa9ae86685501f1d3a0aa22121 (patch) | |
tree | f85fe896a96cee352dae1d887f690eaabf8c761b | |
parent | 899b0aae9f9afacccfd5963e84df6634e9835b5c (diff) | |
download | meson-731906504efb57aa9ae86685501f1d3a0aa22121.zip meson-731906504efb57aa9ae86685501f1d3a0aa22121.tar.gz meson-731906504efb57aa9ae86685501f1d3a0aa22121.tar.bz2 |
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
14 files changed, 140 insertions, 15 deletions
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 4fbb837..358dd8f 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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: diff --git a/run_unittests.py b/run_unittests.py index 5da2798..ebead66 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -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): ''' diff --git a/test cases/common/206 subproject with features/meson.build b/test cases/common/206 subproject with features/meson.build new file mode 100644 index 0000000..5bdfefb --- /dev/null +++ b/test cases/common/206 subproject with features/meson.build @@ -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') diff --git a/test cases/common/206 subproject with features/meson_options.txt b/test cases/common/206 subproject with features/meson_options.txt new file mode 100644 index 0000000..a46e5fb --- /dev/null +++ b/test cases/common/206 subproject with features/meson_options.txt @@ -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') diff --git a/test cases/common/206 subproject with features/nothing.c b/test cases/common/206 subproject with features/nothing.c new file mode 100644 index 0000000..77750c2 --- /dev/null +++ b/test cases/common/206 subproject with features/nothing.c @@ -0,0 +1,4 @@ +int main(int argc, char const *argv[]) +{ + return 0; +}
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build b/test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build new file mode 100644 index 0000000..fa6b011 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/auto_sub_with_missing_dep/meson.build @@ -0,0 +1,3 @@ +project('sub', 'c') + +dependency('no_way_this_exists', required: true)
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build new file mode 100644 index 0000000..933001a --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/meson.build @@ -0,0 +1,3 @@ +lib = static_library('sub', 'sub.c') + +libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib)
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c new file mode 100644 index 0000000..068a5b8 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.c @@ -0,0 +1,5 @@ +#include "sub.h" + +int sub() { + return 0; +} diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h new file mode 100644 index 0000000..f1ab0e1 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/lib/sub.h @@ -0,0 +1,6 @@ +#ifndef SUB_H +#define SUB_H + +int sub(); + +#endif diff --git a/test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build b/test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build new file mode 100644 index 0000000..65fef03 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/disabled_sub/meson.build @@ -0,0 +1,3 @@ +project('disabled_sub', 'c') + +subdir('lib')
\ No newline at end of file diff --git a/test cases/common/206 subproject with features/subprojects/sub/lib/meson.build b/test cases/common/206 subproject with features/subprojects/sub/lib/meson.build new file mode 100644 index 0000000..731d22b --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/lib/meson.build @@ -0,0 +1,2 @@ +lib = static_library('sub', 'sub.c') +libSub = declare_dependency(include_directories: include_directories('.'), link_with: lib) diff --git a/test cases/common/206 subproject with features/subprojects/sub/lib/sub.c b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.c new file mode 100644 index 0000000..ed78306 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.c @@ -0,0 +1,5 @@ +#include "sub.h" + +int sub() { + return 0; +} diff --git a/test cases/common/206 subproject with features/subprojects/sub/lib/sub.h b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.h new file mode 100644 index 0000000..f1ab0e1 --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/lib/sub.h @@ -0,0 +1,6 @@ +#ifndef SUB_H +#define SUB_H + +int sub(); + +#endif diff --git a/test cases/common/206 subproject with features/subprojects/sub/meson.build b/test cases/common/206 subproject with features/subprojects/sub/meson.build new file mode 100644 index 0000000..31882ac --- /dev/null +++ b/test cases/common/206 subproject with features/subprojects/sub/meson.build @@ -0,0 +1,3 @@ +project('sub', 'c') + +subdir('lib')
\ No newline at end of file |