Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interpreter: deprecate using an array for project(license:) #9940

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/markdown/snippets/license_as_array.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## project license keyword argument as an array has been deprecated

Meson has traditionally supported using either a string or an array of strings
as the `license` argument to `project`. More recently it has been strongly
suggested to use an SPDX license expression string instead. SPDX is now an ISO
standard, and is well understood outside of Meson. When used in a Software Bill
of Materials it is natively understood by many tools. An array of licenses is
ambiguous, does `['Apache-2.0', 'GPL-2.0-only']` mean `Apache-2.0 AND GPL-2.0-only`
or `Apache-2.0 OR GPL-2.0-only`? These mean very different things, as the first
is widely believed to be incompatible, and the latter is widely understood to
mean "Apache-2.0 in general, but you may use it as GPL-2.0 for projects using
that license", as Apache-2.0 and GPL-2.0 are generally understood to be
incompatible.

Because of this ambiguity, passing an array has been deprecated and will be
removed in the future.
3 changes: 2 additions & 1 deletion docs/yaml/functions/project.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ kwargs:
licenses here. This is not recommended, as it is ambiguous: `license :
['Apache-2.0', 'GPL-2.0-only']` instead use an SPDX espression: `license
: 'Apache-2.0 OR GPL-2.0-only'`, which makes it clear that the license
mean OR, not AND.
mean OR, not AND. This has been deprecated as of Meson 0.63.0, and will
be removed in the future.

Note that the text is informal and is only written to the dependency
manifest. Meson does not do any license validation, you are responsible
Expand Down
9 changes: 8 additions & 1 deletion mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ def set_backend(self) -> None:
validator=_project_version_validator,
convertor=lambda x: x[0] if isinstance(x, list) else x,
),
KwargInfo('license', ContainerTypeInfo(list, str), default=['unknown'], listify=True),
KwargInfo('license', (str, ContainerTypeInfo(list, str)), default='unknown'),
KwargInfo('subproject_dir', str, default='subprojects'),
)
def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str]], kwargs: 'kwargs.Project') -> None:
Expand Down Expand Up @@ -1189,6 +1189,13 @@ def func_project(self, node: mparser.FunctionNode, args: T.Tuple[str, T.List[str
if self.build.project_version is None:
self.build.project_version = self.project_version
proj_license = kwargs['license']
if isinstance(proj_license, list):
FeatureDeprecated.single_use(
'project license as an array of strings', '0.63.0', self.subproject,
'Use an SPDX license expression with AND and/or OR instead', node,
always_warn=True)
else:
proj_license = [proj_license]
self.build.dep_manifest[proj_name] = build.DepManifest(self.project_version, proj_license)
if self.subproject in self.build.projects:
raise InvalidCode('Second call to project().')
Expand Down
2 changes: 1 addition & 1 deletion mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class Project(TypedDict):
version: T.Optional[FileOrString]
meson_version: T.Optional[str]
default_options: T.List[str]
license: T.List[str]
license: T.Union[str, T.List[str]]
subproject_dir: str


Expand Down
57 changes: 41 additions & 16 deletions mesonbuild/interpreterbase/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,8 +600,21 @@ def get_target_version(subproject: str) -> str:
def check_version(target_version: str, feature_version: str) -> bool:
pass

def use(self, subproject: 'SubProject', location: T.Optional['mparser.BaseNode'] = None) -> None:
def use(self, subproject: 'SubProject', location: T.Optional['mparser.BaseNode'] = None,
always_warn: bool = False) -> None:
"""Generate a warning if necessary.

:param subproject: The subproject this warning is for
:param location: The location node that generated the warning, defaults to None
:param always_warn: If set to True a warning is always generated.
This is useful for cases where the deprecated behavior has always
had a replacement, but we still wish to signal the version in which
the behavior was deprecated. Defaults to False
"""
tv = self.get_target_version(subproject)
if always_warn:
self.log_usage_warning(tv, location, always=always_warn)
return
# No target version
if tv == '':
return
Expand All @@ -618,8 +631,6 @@ def use(self, subproject: 'SubProject', location: T.Optional['mparser.BaseNode']
feature_key = (self.feature_name, location)
if feature_key in register[self.feature_version]:
# Don't warn about the same feature multiple times
# FIXME: This is needed to prevent duplicate warnings, but also
# means we won't warn about a feature used in multiple places.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, but the actual fix was in 7a033c7.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. the discussion at #9488 (comment)

return
register[self.feature_version].add(feature_key)
# Target version is new enough, don't warn even if it is registered for notice
Expand All @@ -645,7 +656,8 @@ def report(cls, subproject: str) -> None:
if '\n' in warning_str:
mlog.warning(warning_str)

def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode']) -> None:
def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode'],
always: bool = False) -> None:
raise InterpreterException('log_usage_warning not implemented')

@staticmethod
Expand All @@ -668,9 +680,10 @@ def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:

@classmethod
def single_use(cls, feature_name: str, version: str, subproject: 'SubProject',
extra_message: str = '', location: T.Optional['mparser.BaseNode'] = None) -> None:
extra_message: str = '', location: T.Optional['mparser.BaseNode'] = None,
always_warn: bool = False) -> None:
"""Oneline version that instantiates and calls use()."""
cls(feature_name, version, extra_message).use(subproject, location)
cls(feature_name, version, extra_message).use(subproject, location, always_warn)


class FeatureNew(FeatureCheckBase):
Expand All @@ -693,7 +706,9 @@ def get_warning_str_prefix(tv: str) -> str:
def get_notice_str_prefix(tv: str) -> str:
return ''

def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode']) -> None:
def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode'],
always: bool = False) -> None:
assert not always, "Shouldn't be used with FeatureNew, only FeatureDeprecated"
args = [
'Project targets', f"'{tv}'",
'but uses feature introduced in',
Expand All @@ -702,7 +717,7 @@ def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode']) -
]
if self.extra_message:
args.append(self.extra_message)
mlog.warning(*args, location=location)
mlog.warning(*args, location=location, fatal=not always)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be dropped. The default is True, but we're setting it to False iff it is an always_warn.

This means that a deprecation for something that is always deprecated and always wrong and always should be replaced... does NOT trigger --fatal-meson-warnings, even though it really should.


class FeatureDeprecated(FeatureCheckBase):
"""Checks for deprecated features"""
Expand All @@ -726,16 +741,26 @@ def get_warning_str_prefix(tv: str) -> str:
def get_notice_str_prefix(tv: str) -> str:
return 'Future-deprecated features used:'

def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode']) -> None:
args = [
'Project targets', f"'{tv}'",
'but uses feature deprecated since',
f"'{self.feature_version}':",
f'{self.feature_name}.',
]
def log_usage_warning(self, tv: str, location: T.Optional['mparser.BaseNode'],
always: bool = False) -> None:
if always:
args = [
'Project uses feature',
self.feature_name,
'which was deprecated in',
f"'{self.feature_version}',",
'and can safely be replaced without causing a regression.'
]
else:
args = [
'Project targets', f"'{tv}'",
'but uses feature deprecated since',
f"'{self.feature_version}':",
f'{self.feature_name}.',
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why we need 2 different messages here, what's wrong with the original one? Also it probably should be fatal when targeting Meson >=0.63 but since that's inside project() I don't think the target meson version is set already, is it?

Sorry for nitpicking, maybe I'm just overthinking this simple PR tbh.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the normal message makes sense in this case, since you'd get a warning like: Project is targing >= 0.52 but using a feature deprecated since 0.63. That seems confusing to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the dedicated warning message makes sense.

However I also agree with @xclaesse that it seems weird to mark it as non-fatal, because if we always warn about it and can always use the better non-deprecated version of it, then we also do want it to trigger --fatal-meson-warnings.

if self.extra_message:
args.append(self.extra_message)
mlog.warning(*args, location=location)
mlog.warning(*args, location=location, fatal=not always)


# This cannot be a dataclass due to https://github.com/python/mypy/issues/5374
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The use of 0.52 is intentional, as the warning should always be triggered
project('foo', meson_version : '>= 0.52', license : ['MIT', 'BSD2'])
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"stdout": [
{
"line": "test cases/warning/7 deprecated project license array/meson.build:2: WARNING: Project uses feature project license as an array of strings which was deprecated in '0.63.0', and can safely be replaced without causing a regression. Use an SPDX license expression with AND and/or OR instead"
}
]
}