diff --git a/classes/licenses.yaml b/classes/licenses.yaml new file mode 100644 index 00000000..ea3f941d --- /dev/null +++ b/classes/licenses.yaml @@ -0,0 +1,36 @@ +metaEnvironment: + PKG_LICENSE: "$(pkg_license)" + +privateEnvironment: + PKG_LICENSE_PATH: "$(pkg_license_paths)" + +buildVars: [PKG_LICENSE, PKG_LICENSE_PATH] +buildScript: | + if [ -z ${PKG_LICENSE:-} ]; then + echo "PKG_LICENSE is missing!" 1>&2 + exit 1 + fi + mkdir -p .bob + if [[ ${PKG_LICENSE} == LicenseRef* ]]; then + ID=${PKG_LICENSE#LicenseRef-} + if [[ $ID =~ ^[A-Za-z0-9.\-]+$ ]]; then + mkdir -p .bob/licenses/$ID + else + echo "LicenseRef contains invalid characters: ${PKG_LICENSE}" + false + fi + while read -r lic; do + _path="" + _file=${lic} + if [[ ${lic} == *:* ]]; then + IFS=: read -r _path _file <<< ${lic} + fi + mkdir -p .bob/licenses/$ID/${_path:-} + cp $1/$_file .bob/licenses/$ID/${_path:-} + done <<< ${PKG_LICENSE_PATH} + else + echo "${PKG_LICENSE}" >> .bob/license + fi + +packageScript: | + cp -r $1/.bob . diff --git a/config.yaml b/config.yaml index ca8734ee..2ad7abd3 100644 --- a/config.yaml +++ b/config.yaml @@ -6,3 +6,4 @@ plugins: - vsenv - msbuild - config + - licenses diff --git a/plugins/licenses.py b/plugins/licenses.py new file mode 100644 index 00000000..0d60a1e3 --- /dev/null +++ b/plugins/licenses.py @@ -0,0 +1,83 @@ +# Defines the "License" recipe key that can be used to describe the package license. +# If the package uses a vanilla license with a known spdx.id the license can be specified as: +# +# License: "GPL-3.0" +# +# and for custom licenses with a license file: +# +# License: +# Id: "devel.m4" +# Paths: "COPYING" +# +# The plugin also offers string functions to get these information back into the environment +# to be used by the licenses class. + +# To enable the validation check of the license expression the license-expression module must be +# installed. (e.g. 'pip install license-expression'). + +from bob.errors import ParseError +from bob.input import PluginState, PluginProperty +from bob.tty import Warn +import schema + +def expression_check(license): + try: + get_spdx_licensing().parse(license, validate=True, strict=True) + except ExpressionError as e: + # a raised exception message is not shown (?) + print(e) + return False + else: + return True + +def dummyCheck (license): + return True + +try: + from license_expression import get_spdx_licensing, ExpressionError +except ImportError as e: + Warn ("Unable to import license_expression module. License expressions will not be checked!").warn() + license_check = dummyCheck +else: + license_check = expression_check + +LICENSE_SHEMA = schema.Schema(schema.Or(str, schema.Schema({'Id' : str, 'Paths' : str}))) + +class LicenseProperty(PluginProperty): + @staticmethod + def validate(data): + try: + lic = LICENSE_SHEMA.validate(data) + except schema.SchemaError as e: + print(e) + return False + if isinstance(lic, str): + return license_check(lic) + return True + +def pkgLicense(args, env, recipe, **kwargs): + lic = recipe.getPluginProperties().get('License').getValue() + if lic is not None: + if isinstance(lic, str): + return lic + else: + return "LicenseRef-"+lic.get('Id') + return "UNSET" + #raise ParseError(f"{recipe.getName()} has no LICENSE") + +def pkgLicensePaths(args, env, recipe, **kwargs): + lic = recipe.getPluginProperties().get('License').getValue() + if lic and isinstance(lic, dict): + return lic.get('Paths', "") + return "" + +manifest = { + 'apiVersion' : "1.1", + 'properties' : { + 'License' : LicenseProperty, + }, + 'stringFunctions' : { + 'pkg_license' : pkgLicense, + 'pkg_license_paths' : pkgLicensePaths + } +} diff --git a/recipes/devel/m4.yaml b/recipes/devel/m4.yaml index 3f54c836..dc5f0535 100644 --- a/recipes/devel/m4.yaml +++ b/recipes/devel/m4.yaml @@ -1,5 +1,7 @@ inherit: [autotools, patch] +License: "GPL-3.0" + metaEnvironment: PKG_VERSION: "1.4.20"