Skip to content

Commit

Permalink
Merge pull request mesonbuild#3823 from mesonbuild/nirbheek/fix-featu…
Browse files Browse the repository at this point in the history
…renew-subprojects

Nirbheek/fix featurenew subprojects
  • Loading branch information
jpakkane authored Jul 2, 2018
2 parents 1ec3c79 + cc58fda commit 1c44afd
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 153 deletions.
1 change: 1 addition & 0 deletions data/syntax-highlighting/vim/syntax/meson.vim
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ syn keyword mesonBuiltin
\ custom_target
\ declare_dependency
\ dependency
\ disabler
\ environment
\ error
\ executable
Expand Down
14 changes: 14 additions & 0 deletions docs/markdown/Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ Do not merge head back to your branch. Any merge commits in your pull
request make it not acceptable for merging into master and you must
remove them.

## Special procedure for new features

Every new feature requires some extra steps, namely:

- Must include a project test under `test cases/`, or if that's not
possible or if the test requires a special environment, it must go
into `run_unittests.py`.
- Must be registered with the [FeatureChecks framework](Release-notes-for-0.47.0.md#Feature_detection_based_on_meson_version_in_project)
that will warn the user if they try to use a new feature while
targetting an older meson version.
- Needs a release note snippet inside `docs/markdown/snippets/` with
a heading and a brief paragraph explaining what the feature does
with an example.

## Acceptance and merging

The kind of review and acceptance any merge proposal gets depends on
Expand Down
6 changes: 3 additions & 3 deletions docs/markdown/snippets/feature_new.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ Project name: featurenew
Project version: undefined
Build machine cpu family: x86_64
Build machine cpu: x86_64
WARNING: Project targetting '>=0.43' but tried to use feature introduced in '0.44.0': get_unquoted
WARNING: Project targetting '>=0.43' but tried to use feature introduced in '0.44.0': configuration_data.get_unquoted()
Message: bar
Build targets in project: 0
Minimum version of features used:
0.44.0: {'get_unquoted'}
WARNING: Project specifies a minimum meson_version '>=0.43' which conflicts with:
* 0.44.0: {'configuration_data.get_unquoted()'}
```
2 changes: 1 addition & 1 deletion mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1780,7 +1780,7 @@ def process_kwargs(self, kwargs):
'when installing a target')

if isinstance(kwargs['install_dir'], list):
FeatureNew('multiple install_dir for custom_target', '0.40.0').use()
FeatureNew('multiple install_dir for custom_target', '0.40.0').use(self.subproject)
# If an item in this list is False, the output corresponding to
# the list index of that item will not be installed
self.install_dir = typeslistify(kwargs['install_dir'], (str, bool))
Expand Down
5 changes: 0 additions & 5 deletions mesonbuild/dependencies/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,9 @@
ConfigToolDependency,
)

from ..interpreterbase import FeatureNew

class MPIDependency(ExternalDependency):

@FeatureNew('MPI Dependency', '0.42.0')
def __init__(self, environment, kwargs):
language = kwargs.get('language', 'c')
super().__init__('mpi', environment, language, kwargs)
Expand Down Expand Up @@ -252,7 +250,6 @@ class OpenMPDependency(ExternalDependency):
'199810': '1.0',
}

@FeatureNew('OpenMP Dependency', '0.46.0')
def __init__(self, environment, kwargs):
language = kwargs.get('language')
super().__init__('openmp', environment, language, kwargs)
Expand Down Expand Up @@ -433,7 +430,6 @@ def get_pkgconfig_variable(self, variable_name, kwargs):

class PcapDependency(ExternalDependency):

@FeatureNew('Pcap Dependency', '0.42.0')
def __init__(self, environment, kwargs):
super().__init__('pcap', environment, None, kwargs)

Expand Down Expand Up @@ -517,7 +513,6 @@ def get_methods():


class LibWmfDependency(ExternalDependency):
@FeatureNew('LibWMF Dependency', '0.44.0')
def __init__(self, environment, kwargs):
super().__init__('libwmf', environment, None, kwargs)

Expand Down
2 changes: 0 additions & 2 deletions mesonbuild/dependencies/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
from .base import ExtraFrameworkDependency, PkgConfigDependency
from .base import ConfigToolDependency

from ..interpreterbase import FeatureNew

class GLDependency(ExternalDependency):
def __init__(self, environment, kwargs):
Expand Down Expand Up @@ -516,7 +515,6 @@ def get_requested(self, kwargs):

class VulkanDependency(ExternalDependency):

@FeatureNew('Vulkan Dependency', '0.42.0')
def __init__(self, environment, kwargs):
super().__init__('vulkan', environment, None, kwargs)

Expand Down
90 changes: 55 additions & 35 deletions mesonbuild/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ def stringifyUserArguments(args):


class ObjectHolder:
def __init__(self, obj):
def __init__(self, obj, subproject=None):
self.held_object = obj
self.subproject = subproject

def __repr__(self):
return '<Holder: {!r}>'.format(self.held_object)
Expand Down Expand Up @@ -210,8 +211,8 @@ class ConfigureFileHolder(InterpreterObject, ObjectHolder):

def __init__(self, subdir, sourcename, targetname, configuration_data):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, build.ConfigureFile(subdir, sourcename,
targetname, configuration_data))
obj = build.ConfigureFile(subdir, sourcename, targetname, configuration_data)
ObjectHolder.__init__(self, obj)


class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder):
Expand Down Expand Up @@ -254,10 +255,10 @@ def prepend_method(self, args, kwargs):


class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder):
def __init__(self):
def __init__(self, pv):
MutableInterpreterObject.__init__(self)
self.used = False # These objects become immutable after use in configure_file.
ObjectHolder.__init__(self, build.ConfigurationData())
ObjectHolder.__init__(self, build.ConfigurationData(), pv)
self.methods.update({'set': self.set_method,
'set10': self.set10_method,
'set_quoted': self.set_quoted_method,
Expand Down Expand Up @@ -328,7 +329,7 @@ def get_method(self, args, kwargs):
return args[1]
raise InterpreterException('Entry %s not in configuration data.' % name)

@FeatureNew('configuration_data.get_unquoted', '0.44.0')
@FeatureNew('configuration_data.get_unquoted()', '0.44.0')
def get_unquoted_method(self, args, kwargs):
if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get method takes one or two arguments.')
Expand Down Expand Up @@ -363,9 +364,9 @@ def merge_from_method(self, args, kwargs):
# these wrappers.

class DependencyHolder(InterpreterObject, ObjectHolder):
def __init__(self, dep):
def __init__(self, dep, pv):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, dep)
ObjectHolder.__init__(self, dep, pv)
self.methods.update({'found': self.found_method,
'type_name': self.type_name_method,
'version': self.version_method,
Expand Down Expand Up @@ -416,12 +417,13 @@ def configtool_method(self, args, kwargs):
@noPosargs
@permittedKwargs(permitted_method_kwargs['partial_dependency'])
def partial_dependency_method(self, args, kwargs):
return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
pdep = self.held_object.get_partial_dependency(**kwargs)
return DependencyHolder(pdep, self.subproject)

class InternalDependencyHolder(InterpreterObject, ObjectHolder):
def __init__(self, dep):
def __init__(self, dep, pv):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, dep)
ObjectHolder.__init__(self, dep, pv)
self.methods.update({'found': self.found_method,
'version': self.version_method,
'partial_dependency': self.partial_dependency_method,
Expand All @@ -441,7 +443,8 @@ def version_method(self, args, kwargs):
@noPosargs
@permittedKwargs(permitted_method_kwargs['partial_dependency'])
def partial_dependency_method(self, args, kwargs):
return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
pdep = self.held_object.get_partial_dependency(**kwargs)
return DependencyHolder(pdep, self.subproject)

class ExternalProgramHolder(InterpreterObject, ObjectHolder):
def __init__(self, ep):
Expand Down Expand Up @@ -470,9 +473,9 @@ def get_name(self):
return self.held_object.get_name()

class ExternalLibraryHolder(InterpreterObject, ObjectHolder):
def __init__(self, el):
def __init__(self, el, pv):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, el)
ObjectHolder.__init__(self, el, pv)
self.methods.update({'found': self.found_method,
'partial_dependency': self.partial_dependency_method,
})
Expand Down Expand Up @@ -501,14 +504,15 @@ def get_exe_args(self):
@noPosargs
@permittedKwargs(permitted_method_kwargs['partial_dependency'])
def partial_dependency_method(self, args, kwargs):
return DependencyHolder(self.held_object.get_partial_dependency(**kwargs))
pdep = self.held_object.get_partial_dependency(**kwargs)
return DependencyHolder(pdep, self.subproject)

class GeneratorHolder(InterpreterObject, ObjectHolder):
@FeatureNewKwargs('generator', '0.43.0', ['capture'])
def __init__(self, interpreter, args, kwargs):
def __init__(self, interp, args, kwargs):
self.interpreter = interp
InterpreterObject.__init__(self)
self.interpreter = interpreter
ObjectHolder.__init__(self, build.Generator(args, kwargs))
ObjectHolder.__init__(self, build.Generator(args, kwargs), interp.subproject)
self.methods.update({'process': self.process_method})

@FeatureNewKwargs('generator.process', '0.45.0', ['preserve_path_from'])
Expand Down Expand Up @@ -715,7 +719,7 @@ def __init__(self, held_object):
class TargetHolder(InterpreterObject, ObjectHolder):
def __init__(self, target, interp):
InterpreterObject.__init__(self)
ObjectHolder.__init__(self, target)
ObjectHolder.__init__(self, target, interp.subproject)
self.interpreter = interp

class BuildTargetHolder(TargetHolder):
Expand Down Expand Up @@ -911,10 +915,11 @@ def get_variable_method(self, args, kwargs):
return self.held_object.variables[varname]

class CompilerHolder(InterpreterObject):
def __init__(self, compiler, env):
def __init__(self, compiler, env, subproject):
InterpreterObject.__init__(self)
self.compiler = compiler
self.environment = env
self.subproject = subproject
self.methods.update({'compiles': self.compiles_method,
'links': self.links_method,
'get_id': self.get_id_method,
Expand Down Expand Up @@ -1408,7 +1413,7 @@ def find_library_method(self, args, kwargs):
self.environment,
self.compiler.language,
silent=True)
return ExternalLibraryHolder(lib)
return ExternalLibraryHolder(lib, self.subproject)

search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
for i in search_dirs:
Expand All @@ -1419,7 +1424,7 @@ def find_library_method(self, args, kwargs):
raise InterpreterException('{} library {!r} not found'.format(self.compiler.get_display_language(), libname))
lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
self.compiler.language)
return ExternalLibraryHolder(lib)
return ExternalLibraryHolder(lib, self.subproject)

@permittedKwargs({})
def has_argument_method(self, args, kwargs):
Expand Down Expand Up @@ -1690,7 +1695,7 @@ def get_compiler_method(self, args, kwargs):
else:
clist = self.build.cross_compilers
if cname in clist:
return CompilerHolder(clist[cname], self.build.environment)
return CompilerHolder(clist[cname], self.build.environment, self.interpreter.subproject)
raise InterpreterException('Tried to access compiler for unspecified language "%s".' % cname)

@noPosargs
Expand Down Expand Up @@ -1962,9 +1967,9 @@ def holderify(self, item):
elif isinstance(item, build.Data):
return DataHolder(item)
elif isinstance(item, dependencies.InternalDependency):
return InternalDependencyHolder(item)
return InternalDependencyHolder(item, self.subproject)
elif isinstance(item, dependencies.ExternalDependency):
return DependencyHolder(item)
return DependencyHolder(item, self.subproject)
elif isinstance(item, dependencies.ExternalProgram):
return ExternalProgramHolder(item)
elif hasattr(item, 'held_object'):
Expand Down Expand Up @@ -2079,7 +2084,7 @@ def func_declare_dependency(self, node, args, kwargs):
external dependencies (including libraries) must go to "dependencies".''')
dep = dependencies.InternalDependency(version, incs, compile_args,
link_args, libs, libs_whole, sources, final_deps)
return DependencyHolder(dep)
return DependencyHolder(dep, self.subproject)

@noKwargs
def func_assert(self, node, args, kwargs):
Expand Down Expand Up @@ -2305,7 +2310,7 @@ def func_get_option(self, nodes, args, kwargs):
def func_configuration_data(self, node, args, kwargs):
if args:
raise InterpreterException('configuration_data takes no arguments')
return ConfigurationDataHolder()
return ConfigurationDataHolder(self.subproject)

def set_options(self, default_options):
# Set default options as if they were passed to the command line.
Expand Down Expand Up @@ -2426,10 +2431,11 @@ def func_project(self, node, args, kwargs):

self.build.subproject_dir = self.subproject_dir

mesonlib.project_meson_versions[self.subproject] = ''
if 'meson_version' in kwargs:
cv = coredata.version
pv = kwargs['meson_version']
mesonlib.target_version = pv
mesonlib.project_meson_versions[self.subproject] = pv
if not mesonlib.version_compare(cv, pv):
raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv))
self.build.projects[self.subproject] = proj_name
Expand Down Expand Up @@ -2799,6 +2805,19 @@ def _find_cached_fallback_dep(self, name, dirname, varname, wanted, required):
'dep {}'.format(found, dirname, wanted, name))
return None

def _handle_featurenew_dependencies(self, name):
'Do a feature check on dependencies used by this subproject'
if name == 'mpi':
FeatureNew('MPI Dependency', '0.42.0').use(self.subproject)
elif name == 'pcap':
FeatureNew('Pcap Dependency', '0.42.0').use(self.subproject)
elif name == 'vulkan':
FeatureNew('Vulkan Dependency', '0.42.0').use(self.subproject)
elif name == 'libwmf':
FeatureNew('LibWMF Dependency', '0.44.0').use(self.subproject)
elif name == 'openmp':
FeatureNew('OpenMP Dependency', '0.46.0').use(self.subproject)

@FeatureNewKwargs('dependency', '0.40.0', ['method'])
@FeatureNewKwargs('dependency', '0.38.0', ['default_options'])
@permittedKwargs(permitted_kwargs['dependency'])
Expand All @@ -2810,7 +2829,7 @@ def func_dependency(self, node, args, kwargs):
disabled, required, feature = extract_required_kwarg(kwargs)
if disabled:
mlog.log('Dependency', mlog.bold(display_name), 'skipped: feature', mlog.bold(feature), 'disabled')
return DependencyHolder(NotFoundDependency(self.environment))
return DependencyHolder(NotFoundDependency(self.environment), self.subproject)

# writing just "dependency('')" is an error, because it can only fail
if name == '' and required and 'fallback' not in kwargs:
Expand Down Expand Up @@ -2845,6 +2864,7 @@ def func_dependency(self, node, args, kwargs):
pass
# ... search for it outside the project
elif name != '':
self._handle_featurenew_dependencies(name)
try:
dep = dependencies.find_external_dependency(name, self.environment, kwargs)
except DependencyException as e:
Expand All @@ -2868,7 +2888,7 @@ def func_dependency(self, node, args, kwargs):
# Only store found-deps in the cache
if dep.found():
self.coredata.deps[identifier] = dep
return DependencyHolder(dep)
return DependencyHolder(dep, self.subproject)

@FeatureNew('disabler', '0.44.0')
@noKwargs
Expand Down Expand Up @@ -3012,7 +3032,7 @@ def func_vcs_tag(self, node, args, kwargs):
if 'input' not in kwargs or 'output' not in kwargs:
raise InterpreterException('Keyword arguments input and output must exist')
if 'fallback' not in kwargs:
FeatureNew('Optional fallback in vcs_tag', '0.41.0').use()
FeatureNew('Optional fallback in vcs_tag', '0.41.0').use(self.subproject)
fallback = kwargs.pop('fallback', self.project_version)
if not isinstance(fallback, str):
raise InterpreterException('Keyword argument fallback must be a string.')
Expand Down Expand Up @@ -3064,7 +3084,7 @@ def func_custom_target(self, node, args, kwargs):
if len(args) != 1:
raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name')
if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']):
FeatureNew('substitutions in custom_target depfile', '0.47.0').use()
FeatureNew('substitutions in custom_target depfile', '0.47.0').use(self.subproject)
name = args[0]
kwargs['install_mode'] = self._get_kwarg_install_mode(kwargs)
tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs), self)
Expand Down Expand Up @@ -3649,9 +3669,9 @@ def func_join_paths(self, node, args, kwargs):
def run(self):
super().run()
mlog.log('Build targets in project:', mlog.bold(str(len(self.build.targets))))
FeatureNew.called_features_report()
FeatureDeprecated.called_features_report()
if self.subproject == '':
FeatureNew.report(self.subproject)
FeatureDeprecated.report(self.subproject)
if not self.is_subproject():
self.print_extra_warnings()

def print_extra_warnings(self):
Expand Down
Loading

0 comments on commit 1c44afd

Please sign in to comment.