diff options
Diffstat (limited to 'meta/classes-global/license.bbclass')
-rw-r--r-- | meta/classes-global/license.bbclass | 184 |
1 files changed, 12 insertions, 172 deletions
diff --git a/meta/classes-global/license.bbclass b/meta/classes-global/license.bbclass index b2e0d3faba..af5f1ed41d 100644 --- a/meta/classes-global/license.bbclass +++ b/meta/classes-global/license.bbclass | |||
@@ -18,8 +18,14 @@ LICENSE_CREATE_PACKAGE ??= "0" | |||
18 | LICENSE_PACKAGE_SUFFIX ??= "-lic" | 18 | LICENSE_PACKAGE_SUFFIX ??= "-lic" |
19 | LICENSE_FILES_DIRECTORY ??= "${datadir}/licenses/" | 19 | LICENSE_FILES_DIRECTORY ??= "${datadir}/licenses/" |
20 | 20 | ||
21 | LICENSE_DEPLOY_PATHCOMPONENT = "${SSTATE_PKGARCH}" | ||
22 | LICENSE_DEPLOY_PATHCOMPONENT:class-cross = "native" | ||
23 | LICENSE_DEPLOY_PATHCOMPONENT:class-native = "native" | ||
24 | # Ensure the *value* of SSTATE_PKGARCH is captured as it is used in the output paths | ||
25 | LICENSE_DEPLOY_PATHCOMPONENT[vardepvalue] += "${LICENSE_DEPLOY_PATHCOMPONENT}" | ||
26 | |||
21 | addtask populate_lic after do_patch before do_build | 27 | addtask populate_lic after do_patch before do_build |
22 | do_populate_lic[dirs] = "${LICSSTATEDIR}/${PN}" | 28 | do_populate_lic[dirs] = "${LICSSTATEDIR}/${LICENSE_DEPLOY_PATHCOMPONENT}/${PN}" |
23 | do_populate_lic[cleandirs] = "${LICSSTATEDIR}" | 29 | do_populate_lic[cleandirs] = "${LICSSTATEDIR}" |
24 | 30 | ||
25 | python do_populate_lic() { | 31 | python do_populate_lic() { |
@@ -29,7 +35,7 @@ python do_populate_lic() { | |||
29 | lic_files_paths = find_license_files(d) | 35 | lic_files_paths = find_license_files(d) |
30 | 36 | ||
31 | # The base directory we wrangle licenses to | 37 | # The base directory we wrangle licenses to |
32 | destdir = os.path.join(d.getVar('LICSSTATEDIR'), d.getVar('SSTATE_PKGARCH'), d.getVar('PN')) | 38 | destdir = os.path.join(d.getVar('LICSSTATEDIR'), d.getVar('LICENSE_DEPLOY_PATHCOMPONENT'), d.getVar('PN')) |
33 | copy_license_files(lic_files_paths, destdir) | 39 | copy_license_files(lic_files_paths, destdir) |
34 | info = get_recipe_info(d) | 40 | info = get_recipe_info(d) |
35 | with open(os.path.join(destdir, "recipeinfo"), "w") as f: | 41 | with open(os.path.join(destdir, "recipeinfo"), "w") as f: |
@@ -38,8 +44,7 @@ python do_populate_lic() { | |||
38 | oe.qa.exit_if_errors(d) | 44 | oe.qa.exit_if_errors(d) |
39 | } | 45 | } |
40 | 46 | ||
41 | PSEUDO_IGNORE_PATHS .= ",${@','.join(((d.getVar('COMMON_LICENSE_DIR') or '') + ' ' + (d.getVar('LICENSE_PATH') or '') + ' ' + d.getVar('COREBASE') + '/meta/COPYING').split())}" | 47 | # it would be better to copy them in do_install:append, but find_license_files is python |
42 | # it would be better to copy them in do_install:append, but find_license_filesa is python | ||
43 | python perform_packagecopy:prepend () { | 48 | python perform_packagecopy:prepend () { |
44 | enabled = oe.data.typed_value('LICENSE_CREATE_PACKAGE', d) | 49 | enabled = oe.data.typed_value('LICENSE_CREATE_PACKAGE', d) |
45 | if d.getVar('CLASSOVERRIDE') == 'class-target' and enabled: | 50 | if d.getVar('CLASSOVERRIDE') == 'class-target' and enabled: |
@@ -149,14 +154,14 @@ def find_license_files(d): | |||
149 | # and "with exceptions" being * | 154 | # and "with exceptions" being * |
150 | # we'll just strip out the modifier and put | 155 | # we'll just strip out the modifier and put |
151 | # the base license. | 156 | # the base license. |
152 | find_license(node.s.replace("+", "").replace("*", "")) | 157 | find_licenses(node.s.replace("+", "").replace("*", "")) |
153 | self.generic_visit(node) | 158 | self.generic_visit(node) |
154 | 159 | ||
155 | def visit_Constant(self, node): | 160 | def visit_Constant(self, node): |
156 | find_license(node.value.replace("+", "").replace("*", "")) | 161 | find_licenses(node.value.replace("+", "").replace("*", "")) |
157 | self.generic_visit(node) | 162 | self.generic_visit(node) |
158 | 163 | ||
159 | def find_license(license_type): | 164 | def find_licenses(license_type): |
160 | try: | 165 | try: |
161 | bb.utils.mkdirhier(gen_lic_dest) | 166 | bb.utils.mkdirhier(gen_lic_dest) |
162 | except: | 167 | except: |
@@ -249,171 +254,6 @@ def find_license_files(d): | |||
249 | 254 | ||
250 | return lic_files_paths | 255 | return lic_files_paths |
251 | 256 | ||
252 | def return_spdx(d, license): | ||
253 | """ | ||
254 | This function returns the spdx mapping of a license if it exists. | ||
255 | """ | ||
256 | return d.getVarFlag('SPDXLICENSEMAP', license) | ||
257 | |||
258 | def canonical_license(d, license): | ||
259 | """ | ||
260 | Return the canonical (SPDX) form of the license if available (so GPLv3 | ||
261 | becomes GPL-3.0-only) or the passed license if there is no canonical form. | ||
262 | """ | ||
263 | return d.getVarFlag('SPDXLICENSEMAP', license) or license | ||
264 | |||
265 | def expand_wildcard_licenses(d, wildcard_licenses): | ||
266 | """ | ||
267 | There are some common wildcard values users may want to use. Support them | ||
268 | here. | ||
269 | """ | ||
270 | licenses = set(wildcard_licenses) | ||
271 | mapping = { | ||
272 | "AGPL-3.0*" : ["AGPL-3.0-only", "AGPL-3.0-or-later"], | ||
273 | "GPL-3.0*" : ["GPL-3.0-only", "GPL-3.0-or-later"], | ||
274 | "LGPL-3.0*" : ["LGPL-3.0-only", "LGPL-3.0-or-later"], | ||
275 | } | ||
276 | for k in mapping: | ||
277 | if k in wildcard_licenses: | ||
278 | licenses.remove(k) | ||
279 | for item in mapping[k]: | ||
280 | licenses.add(item) | ||
281 | |||
282 | for l in licenses: | ||
283 | if l in oe.license.obsolete_license_list(): | ||
284 | bb.fatal("Error, %s is an obsolete license, please use an SPDX reference in INCOMPATIBLE_LICENSE" % l) | ||
285 | if "*" in l: | ||
286 | bb.fatal("Error, %s is an invalid license wildcard entry" % l) | ||
287 | |||
288 | return list(licenses) | ||
289 | |||
290 | def incompatible_license_contains(license, truevalue, falsevalue, d): | ||
291 | license = canonical_license(d, license) | ||
292 | bad_licenses = (d.getVar('INCOMPATIBLE_LICENSE') or "").split() | ||
293 | bad_licenses = expand_wildcard_licenses(d, bad_licenses) | ||
294 | return truevalue if license in bad_licenses else falsevalue | ||
295 | |||
296 | def incompatible_pkg_license(d, dont_want_licenses, license): | ||
297 | # Handles an "or" or two license sets provided by | ||
298 | # flattened_licenses(), pick one that works if possible. | ||
299 | def choose_lic_set(a, b): | ||
300 | return a if all(oe.license.license_ok(canonical_license(d, lic), | ||
301 | dont_want_licenses) for lic in a) else b | ||
302 | |||
303 | try: | ||
304 | licenses = oe.license.flattened_licenses(license, choose_lic_set) | ||
305 | except oe.license.LicenseError as exc: | ||
306 | bb.fatal('%s: %s' % (d.getVar('P'), exc)) | ||
307 | |||
308 | incompatible_lic = [] | ||
309 | for l in licenses: | ||
310 | license = canonical_license(d, l) | ||
311 | if not oe.license.license_ok(license, dont_want_licenses): | ||
312 | incompatible_lic.append(license) | ||
313 | |||
314 | return sorted(incompatible_lic) | ||
315 | |||
316 | def incompatible_license(d, dont_want_licenses, package=None): | ||
317 | """ | ||
318 | This function checks if a recipe has only incompatible licenses. It also | ||
319 | take into consideration 'or' operand. dont_want_licenses should be passed | ||
320 | as canonical (SPDX) names. | ||
321 | """ | ||
322 | import oe.license | ||
323 | license = d.getVar("LICENSE:%s" % package) if package else None | ||
324 | if not license: | ||
325 | license = d.getVar('LICENSE') | ||
326 | |||
327 | return incompatible_pkg_license(d, dont_want_licenses, license) | ||
328 | |||
329 | def check_license_flags(d): | ||
330 | """ | ||
331 | This function checks if a recipe has any LICENSE_FLAGS that | ||
332 | aren't acceptable. | ||
333 | |||
334 | If it does, it returns the all LICENSE_FLAGS missing from the list | ||
335 | of acceptable license flags, or all of the LICENSE_FLAGS if there | ||
336 | is no list of acceptable flags. | ||
337 | |||
338 | If everything is is acceptable, it returns None. | ||
339 | """ | ||
340 | |||
341 | def license_flag_matches(flag, acceptlist, pn): | ||
342 | """ | ||
343 | Return True if flag matches something in acceptlist, None if not. | ||
344 | |||
345 | Before we test a flag against the acceptlist, we append _${PN} | ||
346 | to it. We then try to match that string against the | ||
347 | acceptlist. This covers the normal case, where we expect | ||
348 | LICENSE_FLAGS to be a simple string like 'commercial', which | ||
349 | the user typically matches exactly in the acceptlist by | ||
350 | explicitly appending the package name e.g 'commercial_foo'. | ||
351 | If we fail the match however, we then split the flag across | ||
352 | '_' and append each fragment and test until we either match or | ||
353 | run out of fragments. | ||
354 | """ | ||
355 | flag_pn = ("%s_%s" % (flag, pn)) | ||
356 | for candidate in acceptlist: | ||
357 | if flag_pn == candidate: | ||
358 | return True | ||
359 | |||
360 | flag_cur = "" | ||
361 | flagments = flag_pn.split("_") | ||
362 | flagments.pop() # we've already tested the full string | ||
363 | for flagment in flagments: | ||
364 | if flag_cur: | ||
365 | flag_cur += "_" | ||
366 | flag_cur += flagment | ||
367 | for candidate in acceptlist: | ||
368 | if flag_cur == candidate: | ||
369 | return True | ||
370 | return False | ||
371 | |||
372 | def all_license_flags_match(license_flags, acceptlist): | ||
373 | """ Return all unmatched flags, None if all flags match """ | ||
374 | pn = d.getVar('PN') | ||
375 | split_acceptlist = acceptlist.split() | ||
376 | flags = [] | ||
377 | for flag in license_flags.split(): | ||
378 | if not license_flag_matches(flag, split_acceptlist, pn): | ||
379 | flags.append(flag) | ||
380 | return flags if flags else None | ||
381 | |||
382 | license_flags = d.getVar('LICENSE_FLAGS') | ||
383 | if license_flags: | ||
384 | acceptlist = d.getVar('LICENSE_FLAGS_ACCEPTED') | ||
385 | if not acceptlist: | ||
386 | return license_flags.split() | ||
387 | unmatched_flags = all_license_flags_match(license_flags, acceptlist) | ||
388 | if unmatched_flags: | ||
389 | return unmatched_flags | ||
390 | return None | ||
391 | |||
392 | def check_license_format(d): | ||
393 | """ | ||
394 | This function checks if LICENSE is well defined, | ||
395 | Validate operators in LICENSES. | ||
396 | No spaces are allowed between LICENSES. | ||
397 | """ | ||
398 | pn = d.getVar('PN') | ||
399 | licenses = d.getVar('LICENSE') | ||
400 | from oe.license import license_operator, license_operator_chars, license_pattern | ||
401 | |||
402 | elements = list(filter(lambda x: x.strip(), license_operator.split(licenses))) | ||
403 | for pos, element in enumerate(elements): | ||
404 | if license_pattern.match(element): | ||
405 | if pos > 0 and license_pattern.match(elements[pos - 1]): | ||
406 | oe.qa.handle_error('license-format', | ||
407 | '%s: LICENSE value "%s" has an invalid format - license names ' \ | ||
408 | 'must be separated by the following characters to indicate ' \ | ||
409 | 'the license selection: %s' % | ||
410 | (pn, licenses, license_operator_chars), d) | ||
411 | elif not license_operator.match(element): | ||
412 | oe.qa.handle_error('license-format', | ||
413 | '%s: LICENSE value "%s" has an invalid separator "%s" that is not ' \ | ||
414 | 'in the valid list of separators (%s)' % | ||
415 | (pn, licenses, element, license_operator_chars), d) | ||
416 | |||
417 | SSTATETASKS += "do_populate_lic" | 257 | SSTATETASKS += "do_populate_lic" |
418 | do_populate_lic[sstate-inputdirs] = "${LICSSTATEDIR}" | 258 | do_populate_lic[sstate-inputdirs] = "${LICSSTATEDIR}" |
419 | do_populate_lic[sstate-outputdirs] = "${LICENSE_DIRECTORY}/" | 259 | do_populate_lic[sstate-outputdirs] = "${LICENSE_DIRECTORY}/" |