[v2] license: add 'any_incompatible' function

Message ID 20220413160033.8456-1-kergoth@gmail.com
State New
Headers show
Series [v2] license: add 'any_incompatible' function | expand

Commit Message

Christopher Larson April 13, 2022, 4 p.m. UTC
This function returns True if any of the specified packages are skipped due to
incompatible license.

License exceptions are obeyed. The user may specify the package's license for
cross-recipe checks.

This allows for additions to packagegroups only for non-incompatible builds. For
example:

    RDEPENDS_${PN} += "${@'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''}"

Signed-off-by: Christopher Larson <chris_larson@mentor.com>
---
 meta/classes/license.bbclass                  | 38 +++++++++++++++++++
 .../packagegroups/packagegroup-base.bb        |  2 +
 2 files changed, 40 insertions(+)

v2 changes: fixed string packages logic, corrected reference to
apply_pkg_license_exception, and avoided use of ${@} in the function docstring
to avoid bitbake expanding it and recursing.

Comments

Richard Purdie April 13, 2022, 5:44 p.m. UTC | #1
On Wed, 2022-04-13 at 16:00 +0000, Christopher Larson wrote:
> This function returns True if any of the specified packages are skipped due to
> incompatible license.
> 
> License exceptions are obeyed. The user may specify the package's license for
> cross-recipe checks.
> 
> This allows for additions to packagegroups only for non-incompatible builds. For
> example:
> 
>     RDEPENDS_${PN} += "${@'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''}"
> 
> Signed-off-by: Christopher Larson <chris_larson@mentor.com>
> ---
>  meta/classes/license.bbclass                  | 38 +++++++++++++++++++
>  .../packagegroups/packagegroup-base.bb        |  2 +
>  2 files changed, 40 insertions(+)
> 
> v2 changes: fixed string packages logic, corrected reference to
> apply_pkg_license_exception, and avoided use of ${@} in the function docstring
> to avoid bitbake expanding it and recursing.
> 
> diff --git a/meta/classes/license.bbclass b/meta/classes/license.bbclass
> index 0c637e966e..41993b7227 100644
> --- a/meta/classes/license.bbclass
> +++ b/meta/classes/license.bbclass
> @@ -320,6 +320,44 @@ def incompatible_license(d, dont_want_licenses, package=None):
>  
>      return incompatible_pkg_license(d, dont_want_licenses, license)
>  
> +def any_incompatible(d, packages, licensestring=None):
> +    """Return True if any of the packages are skipped due to incompatible license.
> +
> +    License exceptions are obeyed. The user may specify the package's license
> +    for cross-recipe checks.
> +
> +    This allows for additions to packagegroups only for non-incompatible builds.
> +
> +    For example: 'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''
> +    """
> +    import oe.license
> +
> +    if isinstance(packages, str):
> +        packages = packages.split()
> +
> +    bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
> +    if not bad_licenses:
> +        return False
> +    bad_licenses = expand_wildcard_licenses(d, bad_licenses)
> +
> +    if licensestring is None:
> +        licensestring = d.getVar("LICENSE:%s" % package) if package else None
> +        if not licensestring:
> +            licensestring = d.getVar("LICENSE")
> +
> +    exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
> +    for pkg in packages:
> +        remaining_bad_licenses = oe.license.apply_pkg_license_exception(
> +            pkg, bad_licenses, exceptions
> +        )
> +
> +        incompatible_lic = incompatible_pkg_license(
> +            d, remaining_bad_licenses, licensestring
> +        )
> +        if incompatible_lic:
> +            return True
> +    return False
> +
>  def check_license_flags(d):
>      """
>      This function checks if a recipe has any LICENSE_FLAGS that
> diff --git a/meta/recipes-core/packagegroups/packagegroup-base.bb b/meta/recipes-core/packagegroups/packagegroup-base.bb
> index 7489ef61b0..1c97d03c21 100644
> --- a/meta/recipes-core/packagegroups/packagegroup-base.bb
> +++ b/meta/recipes-core/packagegroups/packagegroup-base.bb
> @@ -14,6 +14,8 @@ PACKAGES = ' \
>              packagegroup-distro-base \
>              packagegroup-machine-base \
>              \
> +            ${@"dbench" if not any_incompatible(d, "dbench", "GPL-3.0-only") else ""} \
> +            \
>              ${@bb.utils.contains("MACHINE_FEATURES", "acpi", "packagegroup-base-acpi", "",d)} \
>              ${@bb.utils.contains("MACHINE_FEATURES", "alsa", "packagegroup-base-alsa", "", d)} \
>              ${@bb.utils.contains("MACHINE_FEATURES", "apm", "packagegroup-base-apm", "", d)} \

I suspect you didn't mean to include this piece since dbench isn't in core?

Cheers,

Richard
Christopher Larson April 13, 2022, 6:07 p.m. UTC | #2
It’s just an example, but you’re right, I’ll change to a better one. Thanks!

--
Christopher Larson
chris_larson@mentor.com, chris.larson@siemens.com, kergoth@gmail.com
Principal Software Engineer, Embedded Linux Solutions, Siemens Digital Industries Software
On Apr 13, 2022, 10:44 AM -0700, Richard Purdie <richard.purdie@linuxfoundation.org>, wrote:
> On Wed, 2022-04-13 at 16:00 +0000, Christopher Larson wrote:
> > This function returns True if any of the specified packages are skipped due to
> > incompatible license.
> >
> > License exceptions are obeyed. The user may specify the package's license for
> > cross-recipe checks.
> >
> > This allows for additions to packagegroups only for non-incompatible builds. For
> > example:
> >
> > RDEPENDS_${PN} += "${@'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''}"
> >
> > Signed-off-by: Christopher Larson <chris_larson@mentor.com>
> > ---
> > meta/classes/license.bbclass | 38 +++++++++++++++++++
> > .../packagegroups/packagegroup-base.bb | 2 +
> > 2 files changed, 40 insertions(+)
> >
> > v2 changes: fixed string packages logic, corrected reference to
> > apply_pkg_license_exception, and avoided use of ${@} in the function docstring
> > to avoid bitbake expanding it and recursing.
> >
> > diff --git a/meta/classes/license.bbclass b/meta/classes/license.bbclass
> > index 0c637e966e..41993b7227 100644
> > --- a/meta/classes/license.bbclass
> > +++ b/meta/classes/license.bbclass
> > @@ -320,6 +320,44 @@ def incompatible_license(d, dont_want_licenses, package=None):
> >
> > return incompatible_pkg_license(d, dont_want_licenses, license)
> >
> > +def any_incompatible(d, packages, licensestring=None):
> > + """Return True if any of the packages are skipped due to incompatible license.
> > +
> > + License exceptions are obeyed. The user may specify the package's license
> > + for cross-recipe checks.
> > +
> > + This allows for additions to packagegroups only for non-incompatible builds.
> > +
> > + For example: 'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''
> > + """
> > + import oe.license
> > +
> > + if isinstance(packages, str):
> > + packages = packages.split()
> > +
> > + bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
> > + if not bad_licenses:
> > + return False
> > + bad_licenses = expand_wildcard_licenses(d, bad_licenses)
> > +
> > + if licensestring is None:
> > + licensestring = d.getVar("LICENSE:%s" % package) if package else None
> > + if not licensestring:
> > + licensestring = d.getVar("LICENSE")
> > +
> > + exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
> > + for pkg in packages:
> > + remaining_bad_licenses = oe.license.apply_pkg_license_exception(
> > + pkg, bad_licenses, exceptions
> > + )
> > +
> > + incompatible_lic = incompatible_pkg_license(
> > + d, remaining_bad_licenses, licensestring
> > + )
> > + if incompatible_lic:
> > + return True
> > + return False
> > +
> > def check_license_flags(d):
> > """
> > This function checks if a recipe has any LICENSE_FLAGS that
> > diff --git a/meta/recipes-core/packagegroups/packagegroup-base.bb b/meta/recipes-core/packagegroups/packagegroup-base.bb
> > index 7489ef61b0..1c97d03c21 100644
> > --- a/meta/recipes-core/packagegroups/packagegroup-base.bb
> > +++ b/meta/recipes-core/packagegroups/packagegroup-base.bb
> > @@ -14,6 +14,8 @@ PACKAGES = ' \
> > packagegroup-distro-base \
> > packagegroup-machine-base \
> > \
> > + ${@"dbench" if not any_incompatible(d, "dbench", "GPL-3.0-only") else ""} \
> > + \
> > ${@bb.utils.contains("MACHINE_FEATURES", "acpi", "packagegroup-base-acpi", "",d)} \
> > ${@bb.utils.contains("MACHINE_FEATURES", "alsa", "packagegroup-base-alsa", "", d)} \
> > ${@bb.utils.contains("MACHINE_FEATURES", "apm", "packagegroup-base-apm", "", d)} \
>
> I suspect you didn't mean to include this piece since dbench isn't in core?
>
> Cheers,
>
> Richard
>
Peter Kjellerstedt April 13, 2022, 8:35 p.m. UTC | #3
> -----Original Message-----
> From: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org> On Behalf Of Richard Purdie
> Sent: den 13 april 2022 19:45
> To: Christopher Larson <kergoth@gmail.com>; openembedded-core@lists.openembedded.org
> Cc: Christopher Larson <chris_larson@mentor.com>
> Subject: Re: [OE-core] [PATCH v2] license: add 'any_incompatible' function
> 
> On Wed, 2022-04-13 at 16:00 +0000, Christopher Larson wrote:
> > This function returns True if any of the specified packages are skipped due to
> > incompatible license.
> >
> > License exceptions are obeyed. The user may specify the package's license for
> > cross-recipe checks.
> >
> > This allows for additions to packagegroups only for non-incompatible builds. For
> > example:
> >
> >     RDEPENDS_${PN} += "${@'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''}"
> >
> > Signed-off-by: Christopher Larson <chris_larson@mentor.com>
> > ---
> >  meta/classes/license.bbclass                  | 38 +++++++++++++++++++
> >  .../packagegroups/packagegroup-base.bb        |  2 +
> >  2 files changed, 40 insertions(+)
> >
> > v2 changes: fixed string packages logic, corrected reference to
> > apply_pkg_license_exception, and avoided use of ${@} in the function docstring
> > to avoid bitbake expanding it and recursing.
> >
> > diff --git a/meta/classes/license.bbclass b/meta/classes/license.bbclass
> > index 0c637e966e..41993b7227 100644
> > --- a/meta/classes/license.bbclass
> > +++ b/meta/classes/license.bbclass
> > @@ -320,6 +320,44 @@ def incompatible_license(d, dont_want_licenses, package=None):
> >
> >      return incompatible_pkg_license(d, dont_want_licenses, license)
> >
> > +def any_incompatible(d, packages, licensestring=None):
> > +    """Return True if any of the packages are skipped due to incompatible license.
> > +
> > +    License exceptions are obeyed. The user may specify the package's license
> > +    for cross-recipe checks.
> > +
> > +    This allows for additions to packagegroups only for non-incompatible builds.
> > +
> > +    For example: 'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''
> > +    """
> > +    import oe.license
> > +
> > +    if isinstance(packages, str):
> > +        packages = packages.split()
> > +
> > +    bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
> > +    if not bad_licenses:
> > +        return False
> > +    bad_licenses = expand_wildcard_licenses(d, bad_licenses)
> > +
> > +    if licensestring is None:
> > +        licensestring = d.getVar("LICENSE:%s" % package) if package else None
> > +        if not licensestring:
> > +            licensestring = d.getVar("LICENSE")
> > +
> > +    exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
> > +    for pkg in packages:
> > +        remaining_bad_licenses = oe.license.apply_pkg_license_exception(
> > +            pkg, bad_licenses, exceptions
> > +        )
> > +
> > +        incompatible_lic = incompatible_pkg_license(
> > +            d, remaining_bad_licenses, licensestring
> > +        )
> > +        if incompatible_lic:
> > +            return True
> > +    return False
> > +
> >  def check_license_flags(d):
> >      """
> >      This function checks if a recipe has any LICENSE_FLAGS that
> > diff --git a/meta/recipes-core/packagegroups/packagegroup-base.bb b/meta/recipes-core/packagegroups/packagegroup-base.bb
> > index 7489ef61b0..1c97d03c21 100644
> > --- a/meta/recipes-core/packagegroups/packagegroup-base.bb
> > +++ b/meta/recipes-core/packagegroups/packagegroup-base.bb
> > @@ -14,6 +14,8 @@ PACKAGES = ' \
> >              packagegroup-distro-base \
> >              packagegroup-machine-base \
> >              \
> > +            ${@"dbench" if not any_incompatible(d, "dbench", "GPL-3.0-only") else ""} \

This usage seems error prone at best. What if the license for dbench 
is changed?

Also, the way I would prefer to do something like the above (assuming you 
meant to include it as a dependency in RDEPENDS:${PN} rather than PACKAGES) 
would be to instead unconditionally add it to RRECOMMENDS:${PN}. Then 
the normal licensing support should skip the package if you have the 
incompatible license globally configured, but since it is only a 
recommendation, that should not be a problem. 

However, one thing I just realized that isn't working is if you have a 
recommendation on a package and then only configure incompatible licenses 
per image. This is normally the preferred way to handle incompatible 
licenses as you can determine per image if a package should be allowed to 
be installed or not. But because the installation of the packages is 
handled by the package manager, and the license compliance is handled 
later by bitbake, there is currently nothing telling the package manager 
to skip the installation of the unwanted package due to its license, and 
instead there is an error because bitbake sees that it was installed. :(

> > +            \
> >              ${@bb.utils.contains("MACHINE_FEATURES", "acpi", "packagegroup-base-acpi", "",d)} \
> >              ${@bb.utils.contains("MACHINE_FEATURES", "alsa", "packagegroup-base-alsa", "", d)} \
> >              ${@bb.utils.contains("MACHINE_FEATURES", "apm", "packagegroup-base-apm", "", d)} \
> 
> I suspect you didn't mean to include this piece since dbench isn't in
> core?
> 
> Cheers,
> 
> Richard

//Peter
Christopher Larson April 13, 2022, 8:40 p.m. UTC | #4
On Wed, Apr 13, 2022 at 1:35 PM Peter Kjellerstedt <
peter.kjellerstedt@axis.com> wrote:

> > -----Original Message-----
> > From: openembedded-core@lists.openembedded.org <
> openembedded-core@lists.openembedded.org> On Behalf Of Richard Purdie
> > Sent: den 13 april 2022 19:45
> > To: Christopher Larson <kergoth@gmail.com>;
> openembedded-core@lists.openembedded.org
> > Cc: Christopher Larson <chris_larson@mentor.com>
> > Subject: Re: [OE-core] [PATCH v2] license: add 'any_incompatible'
> function
> >
> > On Wed, 2022-04-13 at 16:00 +0000, Christopher Larson wrote:
> > > This function returns True if any of the specified packages are
> skipped due to
> > > incompatible license.
> > >
> > > License exceptions are obeyed. The user may specify the package's
> license for
> > > cross-recipe checks.
> > >
> > > This allows for additions to packagegroups only for non-incompatible
> builds. For
> > > example:
> > >
> > >     RDEPENDS_${PN} += "${@'dbench' if not any_incompatible(d,
> 'dbench', 'GPL-3.0-only') else ''}"
> > >
> > > Signed-off-by: Christopher Larson <chris_larson@mentor.com>
> > > ---
> > >  meta/classes/license.bbclass                  | 38 +++++++++++++++++++
> > >  .../packagegroups/packagegroup-base.bb        |  2 +
> > >  2 files changed, 40 insertions(+)
> > >
> > > v2 changes: fixed string packages logic, corrected reference to
> > > apply_pkg_license_exception, and avoided use of ${@} in the function
> docstring
> > > to avoid bitbake expanding it and recursing.
> > >
> > > diff --git a/meta/classes/license.bbclass
> b/meta/classes/license.bbclass
> > > index 0c637e966e..41993b7227 100644
> > > --- a/meta/classes/license.bbclass
> > > +++ b/meta/classes/license.bbclass
> > > @@ -320,6 +320,44 @@ def incompatible_license(d, dont_want_licenses,
> package=None):
> > >
> > >      return incompatible_pkg_license(d, dont_want_licenses, license)
> > >
> > > +def any_incompatible(d, packages, licensestring=None):
> > > +    """Return True if any of the packages are skipped due to
> incompatible license.
> > > +
> > > +    License exceptions are obeyed. The user may specify the package's
> license
> > > +    for cross-recipe checks.
> > > +
> > > +    This allows for additions to packagegroups only for
> non-incompatible builds.
> > > +
> > > +    For example: 'dbench' if not any_incompatible(d, 'dbench',
> 'GPL-3.0-only') else ''
> > > +    """
> > > +    import oe.license
> > > +
> > > +    if isinstance(packages, str):
> > > +        packages = packages.split()
> > > +
> > > +    bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
> > > +    if not bad_licenses:
> > > +        return False
> > > +    bad_licenses = expand_wildcard_licenses(d, bad_licenses)
> > > +
> > > +    if licensestring is None:
> > > +        licensestring = d.getVar("LICENSE:%s" % package) if package
> else None
> > > +        if not licensestring:
> > > +            licensestring = d.getVar("LICENSE")
> > > +
> > > +    exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or
> "").split()
> > > +    for pkg in packages:
> > > +        remaining_bad_licenses =
> oe.license.apply_pkg_license_exception(
> > > +            pkg, bad_licenses, exceptions
> > > +        )
> > > +
> > > +        incompatible_lic = incompatible_pkg_license(
> > > +            d, remaining_bad_licenses, licensestring
> > > +        )
> > > +        if incompatible_lic:
> > > +            return True
> > > +    return False
> > > +
> > >  def check_license_flags(d):
> > >      """
> > >      This function checks if a recipe has any LICENSE_FLAGS that
> > > diff --git a/meta/recipes-core/packagegroups/packagegroup-base.bb
> b/meta/recipes-core/packagegroups/packagegroup-base.bb
> > > index 7489ef61b0..1c97d03c21 100644
> > > --- a/meta/recipes-core/packagegroups/packagegroup-base.bb
> > > +++ b/meta/recipes-core/packagegroups/packagegroup-base.bb
> > > @@ -14,6 +14,8 @@ PACKAGES = ' \
> > >              packagegroup-distro-base \
> > >              packagegroup-machine-base \
> > >              \
> > > +            ${@"dbench" if not any_incompatible(d, "dbench",
> "GPL-3.0-only") else ""} \
>
> This usage seems error prone at best. What if the license for dbench
> is changed?
>

It is, but we can't know other recipes licenses, so the options are
limited. It's also not the only similar thing we do along these lines. More
than once we've had to exclude something from a packagegroup based on the
current context and our knowledge that it's not buildable in this context,
such as knowing the required distro features of the recipe or its
unsupported architectures or COMPATIBLE_MACHINE.

I don't love it, but I don't see a lot of alternatives. RRECOMMENDS is an
interesting idea, but I don't believe it works as you'd hope today, and
would presumably require use of BAD_RECOMMENDATIONS for pickier images. I'm
certainly open to the alternatives on this, but in the meantime some
images/packagegroups just aren't buildable at all for incompatible_license
builds right now without risking layer-inclusion-based tweaks in meta-gplv2.

> I suspect you didn't mean to include this piece since dbench isn't in
> > core?
>

Indeed, v3 forthcoming. Thanks!
Christopher Larson April 22, 2022, 4:59 p.m. UTC | #5
On Wed, Apr 13, 2022 at 1:35 PM Peter Kjellerstedt <
peter.kjellerstedt@axis.com> wrote:

> > > +            ${@"dbench" if not any_incompatible(d, "dbench",
> "GPL-3.0-only") else ""} \
>
> This usage seems error prone at best. What if the license for dbench
> is changed?
>
> Also, the way I would prefer to do something like the above (assuming you
> meant to include it as a dependency in RDEPENDS:${PN} rather than
> PACKAGES)
> would be to instead unconditionally add it to RRECOMMENDS:${PN}. Then
> the normal licensing support should skip the package if you have the
> incompatible license globally configured, but since it is only a
> recommendation, that should not be a problem.


The issue is, a missing RRECOMMENDS will currently abort the build. Bitbake
doesn't treat RRECOMMENDS as optional to build, only optional to install,
so this approach is not viable. I'm still submitting v3 of this to get the
job done for now. Thanks for your thoughts, though, I actually agree that
your idea is a better method, it just doesn't work with bitbake as it
stands today!

Patch

diff --git a/meta/classes/license.bbclass b/meta/classes/license.bbclass
index 0c637e966e..41993b7227 100644
--- a/meta/classes/license.bbclass
+++ b/meta/classes/license.bbclass
@@ -320,6 +320,44 @@  def incompatible_license(d, dont_want_licenses, package=None):
 
     return incompatible_pkg_license(d, dont_want_licenses, license)
 
+def any_incompatible(d, packages, licensestring=None):
+    """Return True if any of the packages are skipped due to incompatible license.
+
+    License exceptions are obeyed. The user may specify the package's license
+    for cross-recipe checks.
+
+    This allows for additions to packagegroups only for non-incompatible builds.
+
+    For example: 'dbench' if not any_incompatible(d, 'dbench', 'GPL-3.0-only') else ''
+    """
+    import oe.license
+
+    if isinstance(packages, str):
+        packages = packages.split()
+
+    bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
+    if not bad_licenses:
+        return False
+    bad_licenses = expand_wildcard_licenses(d, bad_licenses)
+
+    if licensestring is None:
+        licensestring = d.getVar("LICENSE:%s" % package) if package else None
+        if not licensestring:
+            licensestring = d.getVar("LICENSE")
+
+    exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
+    for pkg in packages:
+        remaining_bad_licenses = oe.license.apply_pkg_license_exception(
+            pkg, bad_licenses, exceptions
+        )
+
+        incompatible_lic = incompatible_pkg_license(
+            d, remaining_bad_licenses, licensestring
+        )
+        if incompatible_lic:
+            return True
+    return False
+
 def check_license_flags(d):
     """
     This function checks if a recipe has any LICENSE_FLAGS that
diff --git a/meta/recipes-core/packagegroups/packagegroup-base.bb b/meta/recipes-core/packagegroups/packagegroup-base.bb
index 7489ef61b0..1c97d03c21 100644
--- a/meta/recipes-core/packagegroups/packagegroup-base.bb
+++ b/meta/recipes-core/packagegroups/packagegroup-base.bb
@@ -14,6 +14,8 @@  PACKAGES = ' \
             packagegroup-distro-base \
             packagegroup-machine-base \
             \
+            ${@"dbench" if not any_incompatible(d, "dbench", "GPL-3.0-only") else ""} \
+            \
             ${@bb.utils.contains("MACHINE_FEATURES", "acpi", "packagegroup-base-acpi", "",d)} \
             ${@bb.utils.contains("MACHINE_FEATURES", "alsa", "packagegroup-base-alsa", "", d)} \
             ${@bb.utils.contains("MACHINE_FEATURES", "apm", "packagegroup-base-apm", "", d)} \