diff options
Diffstat (limited to 'scripts/lib/recipetool/create.py')
-rw-r--r-- | scripts/lib/recipetool/create.py | 360 |
1 files changed, 232 insertions, 128 deletions
diff --git a/scripts/lib/recipetool/create.py b/scripts/lib/recipetool/create.py index 566c75369a..8e9ff38db6 100644 --- a/scripts/lib/recipetool/create.py +++ b/scripts/lib/recipetool/create.py | |||
@@ -115,8 +115,8 @@ class RecipeHandler(object): | |||
115 | for line in f: | 115 | for line in f: |
116 | if line.startswith('PN:'): | 116 | if line.startswith('PN:'): |
117 | pn = line.split(':', 1)[-1].strip() | 117 | pn = line.split(':', 1)[-1].strip() |
118 | elif line.startswith('FILES_INFO:'): | 118 | elif line.startswith('FILES_INFO:%s:' % pkg): |
119 | val = line.split(':', 1)[1].strip() | 119 | val = line.split(': ', 1)[1].strip() |
120 | dictval = json.loads(val) | 120 | dictval = json.loads(val) |
121 | for fullpth in sorted(dictval): | 121 | for fullpth in sorted(dictval): |
122 | if fullpth.startswith(includedir) and fullpth.endswith('.h'): | 122 | if fullpth.startswith(includedir) and fullpth.endswith('.h'): |
@@ -366,7 +366,7 @@ def supports_srcrev(uri): | |||
366 | def reformat_git_uri(uri): | 366 | def reformat_git_uri(uri): |
367 | '''Convert any http[s]://....git URI into git://...;protocol=http[s]''' | 367 | '''Convert any http[s]://....git URI into git://...;protocol=http[s]''' |
368 | checkuri = uri.split(';', 1)[0] | 368 | checkuri = uri.split(';', 1)[0] |
369 | if checkuri.endswith('.git') or '/git/' in checkuri or re.match('https?://github.com/[^/]+/[^/]+/?$', checkuri): | 369 | if checkuri.endswith('.git') or '/git/' in checkuri or re.match('https?://git(hub|lab).com/[^/]+/[^/]+/?$', checkuri): |
370 | # Appends scheme if the scheme is missing | 370 | # Appends scheme if the scheme is missing |
371 | if not '://' in uri: | 371 | if not '://' in uri: |
372 | uri = 'git://' + uri | 372 | uri = 'git://' + uri |
@@ -423,6 +423,36 @@ def create_recipe(args): | |||
423 | storeTagName = '' | 423 | storeTagName = '' |
424 | pv_srcpv = False | 424 | pv_srcpv = False |
425 | 425 | ||
426 | handled = [] | ||
427 | classes = [] | ||
428 | |||
429 | # Find all plugins that want to register handlers | ||
430 | logger.debug('Loading recipe handlers') | ||
431 | raw_handlers = [] | ||
432 | for plugin in plugins: | ||
433 | if hasattr(plugin, 'register_recipe_handlers'): | ||
434 | plugin.register_recipe_handlers(raw_handlers) | ||
435 | # Sort handlers by priority | ||
436 | handlers = [] | ||
437 | for i, handler in enumerate(raw_handlers): | ||
438 | if isinstance(handler, tuple): | ||
439 | handlers.append((handler[0], handler[1], i)) | ||
440 | else: | ||
441 | handlers.append((handler, 0, i)) | ||
442 | handlers.sort(key=lambda item: (item[1], -item[2]), reverse=True) | ||
443 | for handler, priority, _ in handlers: | ||
444 | logger.debug('Handler: %s (priority %d)' % (handler.__class__.__name__, priority)) | ||
445 | setattr(handler, '_devtool', args.devtool) | ||
446 | handlers = [item[0] for item in handlers] | ||
447 | |||
448 | fetchuri = None | ||
449 | for handler in handlers: | ||
450 | if hasattr(handler, 'process_url'): | ||
451 | ret = handler.process_url(args, classes, handled, extravalues) | ||
452 | if 'url' in handled and ret: | ||
453 | fetchuri = ret | ||
454 | break | ||
455 | |||
426 | if os.path.isfile(source): | 456 | if os.path.isfile(source): |
427 | source = 'file://%s' % os.path.abspath(source) | 457 | source = 'file://%s' % os.path.abspath(source) |
428 | 458 | ||
@@ -431,11 +461,12 @@ def create_recipe(args): | |||
431 | if re.match(r'https?://github.com/[^/]+/[^/]+/archive/.+(\.tar\..*|\.zip)$', source): | 461 | if re.match(r'https?://github.com/[^/]+/[^/]+/archive/.+(\.tar\..*|\.zip)$', source): |
432 | logger.warning('github archive files are not guaranteed to be stable and may be re-generated over time. If the latter occurs, the checksums will likely change and the recipe will fail at do_fetch. It is recommended that you point to an actual commit or tag in the repository instead (using the repository URL in conjunction with the -S/--srcrev option).') | 462 | logger.warning('github archive files are not guaranteed to be stable and may be re-generated over time. If the latter occurs, the checksums will likely change and the recipe will fail at do_fetch. It is recommended that you point to an actual commit or tag in the repository instead (using the repository URL in conjunction with the -S/--srcrev option).') |
433 | # Fetch a URL | 463 | # Fetch a URL |
434 | fetchuri = reformat_git_uri(urldefrag(source)[0]) | 464 | if not fetchuri: |
465 | fetchuri = reformat_git_uri(urldefrag(source)[0]) | ||
435 | if args.binary: | 466 | if args.binary: |
436 | # Assume the archive contains the directory structure verbatim | 467 | # Assume the archive contains the directory structure verbatim |
437 | # so we need to extract to a subdirectory | 468 | # so we need to extract to a subdirectory |
438 | fetchuri += ';subdir=${BP}' | 469 | fetchuri += ';subdir=${BPN}' |
439 | srcuri = fetchuri | 470 | srcuri = fetchuri |
440 | rev_re = re.compile(';rev=([^;]+)') | 471 | rev_re = re.compile(';rev=([^;]+)') |
441 | res = rev_re.search(srcuri) | 472 | res = rev_re.search(srcuri) |
@@ -478,6 +509,9 @@ def create_recipe(args): | |||
478 | storeTagName = params['tag'] | 509 | storeTagName = params['tag'] |
479 | params['nobranch'] = '1' | 510 | params['nobranch'] = '1' |
480 | del params['tag'] | 511 | del params['tag'] |
512 | # Assume 'master' branch if not set | ||
513 | if scheme in ['git', 'gitsm'] and 'branch' not in params and 'nobranch' not in params: | ||
514 | params['branch'] = 'master' | ||
481 | fetchuri = bb.fetch2.encodeurl((scheme, network, path, user, passwd, params)) | 515 | fetchuri = bb.fetch2.encodeurl((scheme, network, path, user, passwd, params)) |
482 | 516 | ||
483 | tmpparent = tinfoil.config_data.getVar('BASE_WORKDIR') | 517 | tmpparent = tinfoil.config_data.getVar('BASE_WORKDIR') |
@@ -527,10 +561,9 @@ def create_recipe(args): | |||
527 | # Remove HEAD reference point and drop remote prefix | 561 | # Remove HEAD reference point and drop remote prefix |
528 | get_branch = [x.split('/', 1)[1] for x in get_branch if not x.startswith('origin/HEAD')] | 562 | get_branch = [x.split('/', 1)[1] for x in get_branch if not x.startswith('origin/HEAD')] |
529 | if 'master' in get_branch: | 563 | if 'master' in get_branch: |
530 | # If it is master, we do not need to append 'branch=master' as this is default. | ||
531 | # Even with the case where get_branch has multiple objects, if 'master' is one | 564 | # Even with the case where get_branch has multiple objects, if 'master' is one |
532 | # of them, we should default take from 'master' | 565 | # of them, we should default take from 'master' |
533 | srcbranch = '' | 566 | srcbranch = 'master' |
534 | elif len(get_branch) == 1: | 567 | elif len(get_branch) == 1: |
535 | # If 'master' isn't in get_branch and get_branch contains only ONE object, then store result into 'srcbranch' | 568 | # If 'master' isn't in get_branch and get_branch contains only ONE object, then store result into 'srcbranch' |
536 | srcbranch = get_branch[0] | 569 | srcbranch = get_branch[0] |
@@ -543,8 +576,8 @@ def create_recipe(args): | |||
543 | # Since we might have a value in srcbranch, we need to | 576 | # Since we might have a value in srcbranch, we need to |
544 | # recontruct the srcuri to include 'branch' in params. | 577 | # recontruct the srcuri to include 'branch' in params. |
545 | scheme, network, path, user, passwd, params = bb.fetch2.decodeurl(srcuri) | 578 | scheme, network, path, user, passwd, params = bb.fetch2.decodeurl(srcuri) |
546 | if srcbranch: | 579 | if scheme in ['git', 'gitsm']: |
547 | params['branch'] = srcbranch | 580 | params['branch'] = srcbranch or 'master' |
548 | 581 | ||
549 | if storeTagName and scheme in ['git', 'gitsm']: | 582 | if storeTagName and scheme in ['git', 'gitsm']: |
550 | # Check srcrev using tag and check validity of the tag | 583 | # Check srcrev using tag and check validity of the tag |
@@ -603,7 +636,7 @@ def create_recipe(args): | |||
603 | splitline = line.split() | 636 | splitline = line.split() |
604 | if len(splitline) > 1: | 637 | if len(splitline) > 1: |
605 | if splitline[0] == 'origin' and scriptutils.is_src_url(splitline[1]): | 638 | if splitline[0] == 'origin' and scriptutils.is_src_url(splitline[1]): |
606 | srcuri = reformat_git_uri(splitline[1]) | 639 | srcuri = reformat_git_uri(splitline[1]) + ';branch=master' |
607 | srcsubdir = 'git' | 640 | srcsubdir = 'git' |
608 | break | 641 | break |
609 | 642 | ||
@@ -636,8 +669,6 @@ def create_recipe(args): | |||
636 | # We'll come back and replace this later in handle_license_vars() | 669 | # We'll come back and replace this later in handle_license_vars() |
637 | lines_before.append('##LICENSE_PLACEHOLDER##') | 670 | lines_before.append('##LICENSE_PLACEHOLDER##') |
638 | 671 | ||
639 | handled = [] | ||
640 | classes = [] | ||
641 | 672 | ||
642 | # FIXME This is kind of a hack, we probably ought to be using bitbake to do this | 673 | # FIXME This is kind of a hack, we probably ought to be using bitbake to do this |
643 | pn = None | 674 | pn = None |
@@ -675,8 +706,10 @@ def create_recipe(args): | |||
675 | if not srcuri: | 706 | if not srcuri: |
676 | lines_before.append('# No information for SRC_URI yet (only an external source tree was specified)') | 707 | lines_before.append('# No information for SRC_URI yet (only an external source tree was specified)') |
677 | lines_before.append('SRC_URI = "%s"' % srcuri) | 708 | lines_before.append('SRC_URI = "%s"' % srcuri) |
709 | shown_checksums = ["%ssum" % s for s in bb.fetch2.SHOWN_CHECKSUM_LIST] | ||
678 | for key, value in sorted(checksums.items()): | 710 | for key, value in sorted(checksums.items()): |
679 | lines_before.append('SRC_URI[%s] = "%s"' % (key, value)) | 711 | if key in shown_checksums: |
712 | lines_before.append('SRC_URI[%s] = "%s"' % (key, value)) | ||
680 | if srcuri and supports_srcrev(srcuri): | 713 | if srcuri and supports_srcrev(srcuri): |
681 | lines_before.append('') | 714 | lines_before.append('') |
682 | lines_before.append('# Modify these as desired') | 715 | lines_before.append('# Modify these as desired') |
@@ -688,7 +721,7 @@ def create_recipe(args): | |||
688 | srcpvprefix = 'svnr' | 721 | srcpvprefix = 'svnr' |
689 | else: | 722 | else: |
690 | srcpvprefix = scheme | 723 | srcpvprefix = scheme |
691 | lines_before.append('PV = "%s+%s${SRCPV}"' % (realpv or '1.0', srcpvprefix)) | 724 | lines_before.append('PV = "%s+%s"' % (realpv or '1.0', srcpvprefix)) |
692 | pv_srcpv = True | 725 | pv_srcpv = True |
693 | if not args.autorev and srcrev == '${AUTOREV}': | 726 | if not args.autorev and srcrev == '${AUTOREV}': |
694 | if os.path.exists(os.path.join(srctree, '.git')): | 727 | if os.path.exists(os.path.join(srctree, '.git')): |
@@ -710,31 +743,12 @@ def create_recipe(args): | |||
710 | lines_after.append('') | 743 | lines_after.append('') |
711 | 744 | ||
712 | if args.binary: | 745 | if args.binary: |
713 | lines_after.append('INSANE_SKIP_${PN} += "already-stripped"') | 746 | lines_after.append('INSANE_SKIP:${PN} += "already-stripped"') |
714 | lines_after.append('') | 747 | lines_after.append('') |
715 | 748 | ||
716 | if args.npm_dev: | 749 | if args.npm_dev: |
717 | extravalues['NPM_INSTALL_DEV'] = 1 | 750 | extravalues['NPM_INSTALL_DEV'] = 1 |
718 | 751 | ||
719 | # Find all plugins that want to register handlers | ||
720 | logger.debug('Loading recipe handlers') | ||
721 | raw_handlers = [] | ||
722 | for plugin in plugins: | ||
723 | if hasattr(plugin, 'register_recipe_handlers'): | ||
724 | plugin.register_recipe_handlers(raw_handlers) | ||
725 | # Sort handlers by priority | ||
726 | handlers = [] | ||
727 | for i, handler in enumerate(raw_handlers): | ||
728 | if isinstance(handler, tuple): | ||
729 | handlers.append((handler[0], handler[1], i)) | ||
730 | else: | ||
731 | handlers.append((handler, 0, i)) | ||
732 | handlers.sort(key=lambda item: (item[1], -item[2]), reverse=True) | ||
733 | for handler, priority, _ in handlers: | ||
734 | logger.debug('Handler: %s (priority %d)' % (handler.__class__.__name__, priority)) | ||
735 | setattr(handler, '_devtool', args.devtool) | ||
736 | handlers = [item[0] for item in handlers] | ||
737 | |||
738 | # Apply the handlers | 752 | # Apply the handlers |
739 | if args.binary: | 753 | if args.binary: |
740 | classes.append('bin_package') | 754 | classes.append('bin_package') |
@@ -743,6 +757,10 @@ def create_recipe(args): | |||
743 | for handler in handlers: | 757 | for handler in handlers: |
744 | handler.process(srctree_use, classes, lines_before, lines_after, handled, extravalues) | 758 | handler.process(srctree_use, classes, lines_before, lines_after, handled, extravalues) |
745 | 759 | ||
760 | # native and nativesdk classes are special and must be inherited last | ||
761 | # If present, put them at the end of the classes list | ||
762 | classes.sort(key=lambda c: c in ("native", "nativesdk")) | ||
763 | |||
746 | extrafiles = extravalues.pop('extrafiles', {}) | 764 | extrafiles = extravalues.pop('extrafiles', {}) |
747 | extra_pn = extravalues.pop('PN', None) | 765 | extra_pn = extravalues.pop('PN', None) |
748 | extra_pv = extravalues.pop('PV', None) | 766 | extra_pv = extravalues.pop('PV', None) |
@@ -867,8 +885,10 @@ def create_recipe(args): | |||
867 | outlines.append('') | 885 | outlines.append('') |
868 | outlines.extend(lines_after) | 886 | outlines.extend(lines_after) |
869 | 887 | ||
888 | outlines = [ line.rstrip('\n') +"\n" for line in outlines] | ||
889 | |||
870 | if extravalues: | 890 | if extravalues: |
871 | _, outlines = oe.recipeutils.patch_recipe_lines(outlines, extravalues, trailing_newline=False) | 891 | _, outlines = oe.recipeutils.patch_recipe_lines(outlines, extravalues, trailing_newline=True) |
872 | 892 | ||
873 | if args.extract_to: | 893 | if args.extract_to: |
874 | scriptutils.git_convert_standalone_clone(srctree) | 894 | scriptutils.git_convert_standalone_clone(srctree) |
@@ -884,7 +904,7 @@ def create_recipe(args): | |||
884 | log_info_cond('Source extracted to %s' % args.extract_to, args.devtool) | 904 | log_info_cond('Source extracted to %s' % args.extract_to, args.devtool) |
885 | 905 | ||
886 | if outfile == '-': | 906 | if outfile == '-': |
887 | sys.stdout.write('\n'.join(outlines) + '\n') | 907 | sys.stdout.write(''.join(outlines) + '\n') |
888 | else: | 908 | else: |
889 | with open(outfile, 'w') as f: | 909 | with open(outfile, 'w') as f: |
890 | lastline = None | 910 | lastline = None |
@@ -892,9 +912,10 @@ def create_recipe(args): | |||
892 | if not lastline and not line: | 912 | if not lastline and not line: |
893 | # Skip extra blank lines | 913 | # Skip extra blank lines |
894 | continue | 914 | continue |
895 | f.write('%s\n' % line) | 915 | f.write('%s' % line) |
896 | lastline = line | 916 | lastline = line |
897 | log_info_cond('Recipe %s has been created; further editing may be required to make it fully functional' % outfile, args.devtool) | 917 | log_info_cond('Recipe %s has been created; further editing may be required to make it fully functional' % outfile, args.devtool) |
918 | tinfoil.modified_files() | ||
898 | 919 | ||
899 | if tempsrc: | 920 | if tempsrc: |
900 | if args.keep_temp: | 921 | if args.keep_temp: |
@@ -917,6 +938,22 @@ def split_value(value): | |||
917 | else: | 938 | else: |
918 | return value | 939 | return value |
919 | 940 | ||
941 | def fixup_license(value): | ||
942 | # Ensure licenses with OR starts and ends with brackets | ||
943 | if '|' in value: | ||
944 | return '(' + value + ')' | ||
945 | return value | ||
946 | |||
947 | def tidy_licenses(value): | ||
948 | """Flat, split and sort licenses""" | ||
949 | from oe.license import flattened_licenses | ||
950 | def _choose(a, b): | ||
951 | str_a, str_b = sorted((" & ".join(a), " & ".join(b)), key=str.casefold) | ||
952 | return ["(%s | %s)" % (str_a, str_b)] | ||
953 | if not isinstance(value, str): | ||
954 | value = " & ".join(value) | ||
955 | return sorted(list(set(flattened_licenses(value, _choose))), key=str.casefold) | ||
956 | |||
920 | def handle_license_vars(srctree, lines_before, handled, extravalues, d): | 957 | def handle_license_vars(srctree, lines_before, handled, extravalues, d): |
921 | lichandled = [x for x in handled if x[0] == 'license'] | 958 | lichandled = [x for x in handled if x[0] == 'license'] |
922 | if lichandled: | 959 | if lichandled: |
@@ -930,10 +967,13 @@ def handle_license_vars(srctree, lines_before, handled, extravalues, d): | |||
930 | lines = [] | 967 | lines = [] |
931 | if licvalues: | 968 | if licvalues: |
932 | for licvalue in licvalues: | 969 | for licvalue in licvalues: |
933 | if not licvalue[0] in licenses: | 970 | license = licvalue[0] |
934 | licenses.append(licvalue[0]) | 971 | lics = tidy_licenses(fixup_license(license)) |
972 | lics = [lic for lic in lics if lic not in licenses] | ||
973 | if len(lics): | ||
974 | licenses.extend(lics) | ||
935 | lic_files_chksum.append('file://%s;md5=%s' % (licvalue[1], licvalue[2])) | 975 | lic_files_chksum.append('file://%s;md5=%s' % (licvalue[1], licvalue[2])) |
936 | if licvalue[0] == 'Unknown': | 976 | if license == 'Unknown': |
937 | lic_unknown.append(licvalue[1]) | 977 | lic_unknown.append(licvalue[1]) |
938 | if lic_unknown: | 978 | if lic_unknown: |
939 | lines.append('#') | 979 | lines.append('#') |
@@ -942,9 +982,7 @@ def handle_license_vars(srctree, lines_before, handled, extravalues, d): | |||
942 | for licfile in lic_unknown: | 982 | for licfile in lic_unknown: |
943 | lines.append('# %s' % licfile) | 983 | lines.append('# %s' % licfile) |
944 | 984 | ||
945 | extra_license = split_value(extravalues.pop('LICENSE', [])) | 985 | extra_license = tidy_licenses(extravalues.pop('LICENSE', '')) |
946 | if '&' in extra_license: | ||
947 | extra_license.remove('&') | ||
948 | if extra_license: | 986 | if extra_license: |
949 | if licenses == ['Unknown']: | 987 | if licenses == ['Unknown']: |
950 | licenses = extra_license | 988 | licenses = extra_license |
@@ -985,7 +1023,7 @@ def handle_license_vars(srctree, lines_before, handled, extravalues, d): | |||
985 | lines.append('# instead of &. If there is any doubt, check the accompanying documentation') | 1023 | lines.append('# instead of &. If there is any doubt, check the accompanying documentation') |
986 | lines.append('# to determine which situation is applicable.') | 1024 | lines.append('# to determine which situation is applicable.') |
987 | 1025 | ||
988 | lines.append('LICENSE = "%s"' % ' & '.join(licenses)) | 1026 | lines.append('LICENSE = "%s"' % ' & '.join(sorted(licenses, key=str.casefold))) |
989 | lines.append('LIC_FILES_CHKSUM = "%s"' % ' \\\n '.join(lic_files_chksum)) | 1027 | lines.append('LIC_FILES_CHKSUM = "%s"' % ' \\\n '.join(lic_files_chksum)) |
990 | lines.append('') | 1028 | lines.append('') |
991 | 1029 | ||
@@ -1002,118 +1040,170 @@ def handle_license_vars(srctree, lines_before, handled, extravalues, d): | |||
1002 | handled.append(('license', licvalues)) | 1040 | handled.append(('license', licvalues)) |
1003 | return licvalues | 1041 | return licvalues |
1004 | 1042 | ||
1005 | def get_license_md5sums(d, static_only=False): | 1043 | def get_license_md5sums(d, static_only=False, linenumbers=False): |
1006 | import bb.utils | 1044 | import bb.utils |
1045 | import csv | ||
1007 | md5sums = {} | 1046 | md5sums = {} |
1008 | if not static_only: | 1047 | if not static_only and not linenumbers: |
1009 | # Gather md5sums of license files in common license dir | 1048 | # Gather md5sums of license files in common license dir |
1010 | commonlicdir = d.getVar('COMMON_LICENSE_DIR') | 1049 | commonlicdir = d.getVar('COMMON_LICENSE_DIR') |
1011 | for fn in os.listdir(commonlicdir): | 1050 | for fn in os.listdir(commonlicdir): |
1012 | md5value = bb.utils.md5_file(os.path.join(commonlicdir, fn)) | 1051 | md5value = bb.utils.md5_file(os.path.join(commonlicdir, fn)) |
1013 | md5sums[md5value] = fn | 1052 | md5sums[md5value] = fn |
1053 | |||
1014 | # The following were extracted from common values in various recipes | 1054 | # The following were extracted from common values in various recipes |
1015 | # (double checking the license against the license file itself, not just | 1055 | # (double checking the license against the license file itself, not just |
1016 | # the LICENSE value in the recipe) | 1056 | # the LICENSE value in the recipe) |
1017 | md5sums['94d55d512a9ba36caa9b7df079bae19f'] = 'GPLv2' | 1057 | |
1018 | md5sums['b234ee4d69f5fce4486a80fdaf4a4263'] = 'GPLv2' | 1058 | # Read license md5sums from csv file |
1019 | md5sums['59530bdf33659b29e73d4adb9f9f6552'] = 'GPLv2' | 1059 | scripts_path = os.path.dirname(os.path.realpath(__file__)) |
1020 | md5sums['0636e73ff0215e8d672dc4c32c317bb3'] = 'GPLv2' | 1060 | for path in (d.getVar('BBPATH').split(':') |
1021 | md5sums['eb723b61539feef013de476e68b5c50a'] = 'GPLv2' | 1061 | + [os.path.join(scripts_path, '..', '..')]): |
1022 | md5sums['751419260aa954499f7abaabaa882bbe'] = 'GPLv2' | 1062 | csv_path = os.path.join(path, 'lib', 'recipetool', 'licenses.csv') |
1023 | md5sums['393a5ca445f6965873eca0259a17f833'] = 'GPLv2' | 1063 | if os.path.isfile(csv_path): |
1024 | md5sums['12f884d2ae1ff87c09e5b7ccc2c4ca7e'] = 'GPLv2' | 1064 | with open(csv_path, newline='') as csv_file: |
1025 | md5sums['8ca43cbc842c2336e835926c2166c28b'] = 'GPLv2' | 1065 | fieldnames = ['md5sum', 'license', 'beginline', 'endline', 'md5'] |
1026 | md5sums['ebb5c50ab7cab4baeffba14977030c07'] = 'GPLv2' | 1066 | reader = csv.DictReader(csv_file, delimiter=',', fieldnames=fieldnames) |
1027 | md5sums['c93c0550bd3173f4504b2cbd8991e50b'] = 'GPLv2' | 1067 | for row in reader: |
1028 | md5sums['9ac2e7cff1ddaf48b6eab6028f23ef88'] = 'GPLv2' | 1068 | if linenumbers: |
1029 | md5sums['4325afd396febcb659c36b49533135d4'] = 'GPLv2' | 1069 | md5sums[row['md5sum']] = ( |
1030 | md5sums['18810669f13b87348459e611d31ab760'] = 'GPLv2' | 1070 | row['license'], row['beginline'], row['endline'], row['md5']) |
1031 | md5sums['d7810fab7487fb0aad327b76f1be7cd7'] = 'GPLv2' # the Linux kernel's COPYING file | 1071 | else: |
1032 | md5sums['bbb461211a33b134d42ed5ee802b37ff'] = 'LGPLv2.1' | 1072 | md5sums[row['md5sum']] = row['license'] |
1033 | md5sums['7fbc338309ac38fefcd64b04bb903e34'] = 'LGPLv2.1' | 1073 | |
1034 | md5sums['4fbd65380cdd255951079008b364516c'] = 'LGPLv2.1' | ||
1035 | md5sums['2d5025d4aa3495befef8f17206a5b0a1'] = 'LGPLv2.1' | ||
1036 | md5sums['fbc093901857fcd118f065f900982c24'] = 'LGPLv2.1' | ||
1037 | md5sums['a6f89e2100d9b6cdffcea4f398e37343'] = 'LGPLv2.1' | ||
1038 | md5sums['d8045f3b8f929c1cb29a1e3fd737b499'] = 'LGPLv2.1' | ||
1039 | md5sums['fad9b3332be894bab9bc501572864b29'] = 'LGPLv2.1' | ||
1040 | md5sums['3bf50002aefd002f49e7bb854063f7e7'] = 'LGPLv2' | ||
1041 | md5sums['9f604d8a4f8e74f4f5140845a21b6674'] = 'LGPLv2' | ||
1042 | md5sums['5f30f0716dfdd0d91eb439ebec522ec2'] = 'LGPLv2' | ||
1043 | md5sums['55ca817ccb7d5b5b66355690e9abc605'] = 'LGPLv2' | ||
1044 | md5sums['252890d9eee26aab7b432e8b8a616475'] = 'LGPLv2' | ||
1045 | md5sums['3214f080875748938ba060314b4f727d'] = 'LGPLv2' | ||
1046 | md5sums['db979804f025cf55aabec7129cb671ed'] = 'LGPLv2' | ||
1047 | md5sums['d32239bcb673463ab874e80d47fae504'] = 'GPLv3' | ||
1048 | md5sums['f27defe1e96c2e1ecd4e0c9be8967949'] = 'GPLv3' | ||
1049 | md5sums['6a6a8e020838b23406c81b19c1d46df6'] = 'LGPLv3' | ||
1050 | md5sums['3b83ef96387f14655fc854ddc3c6bd57'] = 'Apache-2.0' | ||
1051 | md5sums['385c55653886acac3821999a3ccd17b3'] = 'Artistic-1.0 | GPL-2.0' # some perl modules | ||
1052 | md5sums['54c7042be62e169199200bc6477f04d1'] = 'BSD-3-Clause' | ||
1053 | md5sums['bfe1f75d606912a4111c90743d6c7325'] = 'MPL-1.1' | ||
1054 | return md5sums | 1074 | return md5sums |
1055 | 1075 | ||
1056 | def crunch_license(licfile): | 1076 | def crunch_known_licenses(d): |
1057 | ''' | 1077 | ''' |
1058 | Remove non-material text from a license file and then check | 1078 | Calculate the MD5 checksums for the crunched versions of all common |
1059 | its md5sum against a known list. This works well for licenses | 1079 | licenses. Also add additional known checksums. |
1060 | which contain a copyright statement, but is also a useful way | ||
1061 | to handle people's insistence upon reformatting the license text | ||
1062 | slightly (with no material difference to the text of the | ||
1063 | license). | ||
1064 | ''' | 1080 | ''' |
1081 | |||
1082 | crunched_md5sums = {} | ||
1065 | 1083 | ||
1066 | import oe.utils | 1084 | # common licenses |
1085 | crunched_md5sums['ad4e9d34a2e966dfe9837f18de03266d'] = 'GFDL-1.1-only' | ||
1086 | crunched_md5sums['d014fb11a34eb67dc717fdcfc97e60ed'] = 'GFDL-1.2-only' | ||
1087 | crunched_md5sums['e020ca655b06c112def28e597ab844f1'] = 'GFDL-1.3-only' | ||
1067 | 1088 | ||
1068 | # Note: these are carefully constructed! | ||
1069 | license_title_re = re.compile(r'^\(?(#+ *)?(The )?.{1,10} [Ll]icen[sc]e( \(.{1,10}\))?\)?:?$') | ||
1070 | license_statement_re = re.compile(r'^(This (project|software) is( free software)? (released|licen[sc]ed)|(Released|Licen[cs]ed)) under the .{1,10} [Ll]icen[sc]e:?$') | ||
1071 | copyright_re = re.compile('^(#+)? *Copyright .*$') | ||
1072 | |||
1073 | crunched_md5sums = {} | ||
1074 | # The following two were gleaned from the "forever" npm package | 1089 | # The following two were gleaned from the "forever" npm package |
1075 | crunched_md5sums['0a97f8e4cbaf889d6fa51f84b89a79f6'] = 'ISC' | 1090 | crunched_md5sums['0a97f8e4cbaf889d6fa51f84b89a79f6'] = 'ISC' |
1076 | crunched_md5sums['eecf6429523cbc9693547cf2db790b5c'] = 'MIT' | ||
1077 | # https://github.com/vasi/pixz/blob/master/LICENSE | ||
1078 | crunched_md5sums['2f03392b40bbe663597b5bd3cc5ebdb9'] = 'BSD-2-Clause' | ||
1079 | # https://github.com/waffle-gl/waffle/blob/master/LICENSE.txt | 1091 | # https://github.com/waffle-gl/waffle/blob/master/LICENSE.txt |
1080 | crunched_md5sums['e72e5dfef0b1a4ca8a3d26a60587db66'] = 'BSD-2-Clause' | 1092 | crunched_md5sums['50fab24ce589d69af8964fdbfe414c60'] = 'BSD-2-Clause' |
1081 | # https://github.com/spigwitmer/fakeds1963s/blob/master/LICENSE | 1093 | # https://github.com/spigwitmer/fakeds1963s/blob/master/LICENSE |
1082 | crunched_md5sums['8be76ac6d191671f347ee4916baa637e'] = 'GPLv2' | 1094 | crunched_md5sums['88a4355858a1433fea99fae34a44da88'] = 'GPL-2.0-only' |
1083 | # https://github.com/datto/dattobd/blob/master/COPYING | ||
1084 | # http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/GPLv2.TXT | ||
1085 | crunched_md5sums['1d65c5ad4bf6489f85f4812bf08ae73d'] = 'GPLv2' | ||
1086 | # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt | 1095 | # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt |
1087 | # http://git.neil.brown.name/?p=mdadm.git;a=blob;f=COPYING;h=d159169d1050894d3ea3b98e1c965c4058208fe1;hb=HEAD | 1096 | crunched_md5sums['063b5c3ebb5f3aa4c85a2ed18a31fbe7'] = 'GPL-2.0-only' |
1088 | crunched_md5sums['fb530f66a7a89ce920f0e912b5b66d4b'] = 'GPLv2' | ||
1089 | # https://github.com/gkos/nrf24/blob/master/COPYING | ||
1090 | crunched_md5sums['7b6aaa4daeafdfa6ed5443fd2684581b'] = 'GPLv2' | ||
1091 | # https://github.com/josch09/resetusb/blob/master/COPYING | ||
1092 | crunched_md5sums['8b8ac1d631a4d220342e83bcf1a1fbc3'] = 'GPLv3' | ||
1093 | # https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv2.1 | 1097 | # https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv2.1 |
1094 | crunched_md5sums['2ea316ed973ae176e502e2297b574bb3'] = 'LGPLv2.1' | 1098 | crunched_md5sums['7f5202f4d44ed15dcd4915f5210417d8'] = 'LGPL-2.1-only' |
1095 | # unixODBC-2.3.4 COPYING | 1099 | # unixODBC-2.3.4 COPYING |
1096 | crunched_md5sums['1daebd9491d1e8426900b4fa5a422814'] = 'LGPLv2.1' | 1100 | crunched_md5sums['3debde09238a8c8e1f6a847e1ec9055b'] = 'LGPL-2.1-only' |
1097 | # https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv3 | 1101 | # https://github.com/FFmpeg/FFmpeg/blob/master/COPYING.LGPLv3 |
1098 | crunched_md5sums['2ebfb3bb49b9a48a075cc1425e7f4129'] = 'LGPLv3' | 1102 | crunched_md5sums['f90c613c51aa35da4d79dd55fc724ceb'] = 'LGPL-3.0-only' |
1099 | # https://raw.githubusercontent.com/eclipse/mosquitto/v1.4.14/epl-v10 | 1103 | # https://raw.githubusercontent.com/eclipse/mosquitto/v1.4.14/epl-v10 |
1100 | crunched_md5sums['efe2cb9a35826992b9df68224e3c2628'] = 'EPL-1.0' | 1104 | crunched_md5sums['efe2cb9a35826992b9df68224e3c2628'] = 'EPL-1.0' |
1101 | # https://raw.githubusercontent.com/eclipse/mosquitto/v1.4.14/edl-v10 | 1105 | |
1102 | crunched_md5sums['0a9c78c0a398d1bbce4a166757d60387'] = 'EDL-1.0' | 1106 | # https://raw.githubusercontent.com/jquery/esprima/3.1.3/LICENSE.BSD |
1107 | crunched_md5sums['80fa7b56a28e8c902e6af194003220a5'] = 'BSD-2-Clause' | ||
1108 | # https://raw.githubusercontent.com/npm/npm-install-checks/master/LICENSE | ||
1109 | crunched_md5sums['e659f77bfd9002659e112d0d3d59b2c1'] = 'BSD-2-Clause' | ||
1110 | # https://raw.githubusercontent.com/silverwind/default-gateway/4.2.0/LICENSE | ||
1111 | crunched_md5sums['4c641f2d995c47f5cb08bdb4b5b6ea05'] = 'BSD-2-Clause' | ||
1112 | # https://raw.githubusercontent.com/tad-lispy/node-damerau-levenshtein/v1.0.5/LICENSE | ||
1113 | crunched_md5sums['2b8c039b2b9a25f0feb4410c4542d346'] = 'BSD-2-Clause' | ||
1114 | # https://raw.githubusercontent.com/terser/terser/v3.17.0/LICENSE | ||
1115 | crunched_md5sums['8bd23871802951c9ad63855151204c2c'] = 'BSD-2-Clause' | ||
1116 | # https://raw.githubusercontent.com/alexei/sprintf.js/1.0.3/LICENSE | ||
1117 | crunched_md5sums['008c22318c8ea65928bf730ddd0273e3'] = 'BSD-3-Clause' | ||
1118 | # https://raw.githubusercontent.com/Caligatio/jsSHA/v3.2.0/LICENSE | ||
1119 | crunched_md5sums['0e46634a01bfef056892949acaea85b1'] = 'BSD-3-Clause' | ||
1120 | # https://raw.githubusercontent.com/d3/d3-path/v1.0.9/LICENSE | ||
1121 | crunched_md5sums['b5f72aef53d3b2b432702c30b0215666'] = 'BSD-3-Clause' | ||
1122 | # https://raw.githubusercontent.com/feross/ieee754/v1.1.13/LICENSE | ||
1123 | crunched_md5sums['a39327c997c20da0937955192d86232d'] = 'BSD-3-Clause' | ||
1124 | # https://raw.githubusercontent.com/joyent/node-extsprintf/v1.3.0/LICENSE | ||
1125 | crunched_md5sums['721f23a96ff4161ca3a5f071bbe18108'] = 'MIT' | ||
1126 | # https://raw.githubusercontent.com/pvorb/clone/v0.2.0/LICENSE | ||
1127 | crunched_md5sums['b376d29a53c9573006b9970709231431'] = 'MIT' | ||
1128 | # https://raw.githubusercontent.com/andris9/encoding/v0.1.12/LICENSE | ||
1129 | crunched_md5sums['85d8a977ee9d7c5ab4ac03c9b95431c4'] = 'MIT-0' | ||
1130 | # https://raw.githubusercontent.com/faye/websocket-driver-node/0.7.3/LICENSE.md | ||
1131 | crunched_md5sums['b66384e7137e41a9b1904ef4d39703b6'] = 'Apache-2.0' | ||
1132 | # https://raw.githubusercontent.com/less/less.js/v4.1.1/LICENSE | ||
1133 | crunched_md5sums['b27575459e02221ccef97ec0bfd457ae'] = 'Apache-2.0' | ||
1134 | # https://raw.githubusercontent.com/microsoft/TypeScript/v3.5.3/LICENSE.txt | ||
1135 | crunched_md5sums['a54a1a6a39e7f9dbb4a23a42f5c7fd1c'] = 'Apache-2.0' | ||
1136 | # https://raw.githubusercontent.com/request/request/v2.87.0/LICENSE | ||
1137 | crunched_md5sums['1034431802e57486b393d00c5d262b8a'] = 'Apache-2.0' | ||
1138 | # https://raw.githubusercontent.com/dchest/tweetnacl-js/v0.14.5/LICENSE | ||
1139 | crunched_md5sums['75605e6bdd564791ab698fca65c94a4f'] = 'Unlicense' | ||
1140 | # https://raw.githubusercontent.com/stackgl/gl-mat3/v2.0.0/LICENSE.md | ||
1141 | crunched_md5sums['75512892d6f59dddb6d1c7e191957e9c'] = 'Zlib' | ||
1142 | |||
1143 | commonlicdir = d.getVar('COMMON_LICENSE_DIR') | ||
1144 | for fn in sorted(os.listdir(commonlicdir)): | ||
1145 | md5value, lictext = crunch_license(os.path.join(commonlicdir, fn)) | ||
1146 | if md5value not in crunched_md5sums: | ||
1147 | crunched_md5sums[md5value] = fn | ||
1148 | elif fn != crunched_md5sums[md5value]: | ||
1149 | bb.debug(2, "crunched_md5sums['%s'] is already set to '%s' rather than '%s'" % (md5value, crunched_md5sums[md5value], fn)) | ||
1150 | else: | ||
1151 | bb.debug(2, "crunched_md5sums['%s'] is already set to '%s'" % (md5value, crunched_md5sums[md5value])) | ||
1152 | |||
1153 | return crunched_md5sums | ||
1154 | |||
1155 | def crunch_license(licfile): | ||
1156 | ''' | ||
1157 | Remove non-material text from a license file and then calculate its | ||
1158 | md5sum. This works well for licenses that contain a copyright statement, | ||
1159 | but is also a useful way to handle people's insistence upon reformatting | ||
1160 | the license text slightly (with no material difference to the text of the | ||
1161 | license). | ||
1162 | ''' | ||
1163 | |||
1164 | import oe.utils | ||
1165 | |||
1166 | # Note: these are carefully constructed! | ||
1167 | license_title_re = re.compile(r'^#*\(? *(This is )?([Tt]he )?.{0,15} ?[Ll]icen[sc]e( \(.{1,10}\))?\)?[:\.]? ?#*$') | ||
1168 | license_statement_re = re.compile(r'^((This (project|software)|.{1,10}) is( free software)? (released|licen[sc]ed)|(Released|Licen[cs]ed)) under the .{1,10} [Ll]icen[sc]e:?$') | ||
1169 | copyright_re = re.compile(r'^ *[#\*]* *(Modified work |MIT LICENSED )?Copyright ?(\([cC]\))? .*$') | ||
1170 | disclaimer_re = re.compile(r'^ *\*? ?All [Rr]ights [Rr]eserved\.$') | ||
1171 | email_re = re.compile(r'^.*<[\w\.-]*@[\w\.\-]*>$') | ||
1172 | header_re = re.compile(r'^(\/\**!?)? ?[\-=\*]* ?(\*\/)?$') | ||
1173 | tag_re = re.compile(r'^ *@?\(?([Ll]icense|MIT)\)?$') | ||
1174 | url_re = re.compile(r'^ *[#\*]* *https?:\/\/[\w\.\/\-]+$') | ||
1175 | |||
1103 | lictext = [] | 1176 | lictext = [] |
1104 | with open(licfile, 'r', errors='surrogateescape') as f: | 1177 | with open(licfile, 'r', errors='surrogateescape') as f: |
1105 | for line in f: | 1178 | for line in f: |
1106 | # Drop opening statements | 1179 | # Drop opening statements |
1107 | if copyright_re.match(line): | 1180 | if copyright_re.match(line): |
1108 | continue | 1181 | continue |
1182 | elif disclaimer_re.match(line): | ||
1183 | continue | ||
1184 | elif email_re.match(line): | ||
1185 | continue | ||
1186 | elif header_re.match(line): | ||
1187 | continue | ||
1188 | elif tag_re.match(line): | ||
1189 | continue | ||
1190 | elif url_re.match(line): | ||
1191 | continue | ||
1109 | elif license_title_re.match(line): | 1192 | elif license_title_re.match(line): |
1110 | continue | 1193 | continue |
1111 | elif license_statement_re.match(line): | 1194 | elif license_statement_re.match(line): |
1112 | continue | 1195 | continue |
1113 | # Squash spaces, and replace smart quotes, double quotes | 1196 | # Strip comment symbols |
1114 | # and backticks with single quotes | 1197 | line = line.replace('*', '') \ |
1198 | .replace('#', '') | ||
1199 | # Unify spelling | ||
1200 | line = line.replace('sub-license', 'sublicense') | ||
1201 | # Squash spaces | ||
1115 | line = oe.utils.squashspaces(line.strip()) | 1202 | line = oe.utils.squashspaces(line.strip()) |
1203 | # Replace smart quotes, double quotes and backticks with single quotes | ||
1116 | line = line.replace(u"\u2018", "'").replace(u"\u2019", "'").replace(u"\u201c","'").replace(u"\u201d", "'").replace('"', '\'').replace('`', '\'') | 1204 | line = line.replace(u"\u2018", "'").replace(u"\u2019", "'").replace(u"\u201c","'").replace(u"\u201d", "'").replace('"', '\'').replace('`', '\'') |
1205 | # Unify brackets | ||
1206 | line = line.replace("{", "[").replace("}", "]") | ||
1117 | if line: | 1207 | if line: |
1118 | lictext.append(line) | 1208 | lictext.append(line) |
1119 | 1209 | ||
@@ -1124,31 +1214,40 @@ def crunch_license(licfile): | |||
1124 | except UnicodeEncodeError: | 1214 | except UnicodeEncodeError: |
1125 | md5val = None | 1215 | md5val = None |
1126 | lictext = '' | 1216 | lictext = '' |
1127 | license = crunched_md5sums.get(md5val, None) | 1217 | return md5val, lictext |
1128 | return license, md5val, lictext | ||
1129 | 1218 | ||
1130 | def guess_license(srctree, d): | 1219 | def guess_license(srctree, d): |
1131 | import bb | 1220 | import bb |
1132 | md5sums = get_license_md5sums(d) | 1221 | md5sums = get_license_md5sums(d) |
1133 | 1222 | ||
1223 | crunched_md5sums = crunch_known_licenses(d) | ||
1224 | |||
1134 | licenses = [] | 1225 | licenses = [] |
1135 | licspecs = ['*LICEN[CS]E*', 'COPYING*', '*[Ll]icense*', 'LEGAL*', '[Ll]egal*', '*GPL*', 'README.lic*', 'COPYRIGHT*', '[Cc]opyright*', 'e[dp]l-v10'] | 1226 | licspecs = ['*LICEN[CS]E*', 'COPYING*', '*[Ll]icense*', 'LEGAL*', '[Ll]egal*', '*GPL*', 'README.lic*', 'COPYRIGHT*', '[Cc]opyright*', 'e[dp]l-v10'] |
1227 | skip_extensions = (".html", ".js", ".json", ".svg", ".ts", ".go") | ||
1136 | licfiles = [] | 1228 | licfiles = [] |
1137 | for root, dirs, files in os.walk(srctree): | 1229 | for root, dirs, files in os.walk(srctree): |
1138 | for fn in files: | 1230 | for fn in files: |
1231 | if fn.endswith(skip_extensions): | ||
1232 | continue | ||
1139 | for spec in licspecs: | 1233 | for spec in licspecs: |
1140 | if fnmatch.fnmatch(fn, spec): | 1234 | if fnmatch.fnmatch(fn, spec): |
1141 | fullpath = os.path.join(root, fn) | 1235 | fullpath = os.path.join(root, fn) |
1142 | if not fullpath in licfiles: | 1236 | if not fullpath in licfiles: |
1143 | licfiles.append(fullpath) | 1237 | licfiles.append(fullpath) |
1144 | for licfile in licfiles: | 1238 | for licfile in sorted(licfiles): |
1145 | md5value = bb.utils.md5_file(licfile) | 1239 | md5value = bb.utils.md5_file(licfile) |
1146 | license = md5sums.get(md5value, None) | 1240 | license = md5sums.get(md5value, None) |
1147 | if not license: | 1241 | if not license: |
1148 | license, crunched_md5, lictext = crunch_license(licfile) | 1242 | crunched_md5, lictext = crunch_license(licfile) |
1149 | if not license: | 1243 | license = crunched_md5sums.get(crunched_md5, None) |
1244 | if lictext and not license: | ||
1150 | license = 'Unknown' | 1245 | license = 'Unknown' |
1151 | licenses.append((license, os.path.relpath(licfile, srctree), md5value)) | 1246 | logger.info("Please add the following line for '%s' to a 'lib/recipetool/licenses.csv' " \ |
1247 | "and replace `Unknown` with the license:\n" \ | ||
1248 | "%s,Unknown" % (os.path.relpath(licfile, srctree), md5value)) | ||
1249 | if license: | ||
1250 | licenses.append((license, os.path.relpath(licfile, srctree), md5value)) | ||
1152 | 1251 | ||
1153 | # FIXME should we grab at least one source file with a license header and add that too? | 1252 | # FIXME should we grab at least one source file with a license header and add that too? |
1154 | 1253 | ||
@@ -1162,6 +1261,7 @@ def split_pkg_licenses(licvalues, packages, outlines, fallback_licenses=None, pn | |||
1162 | """ | 1261 | """ |
1163 | pkglicenses = {pn: []} | 1262 | pkglicenses = {pn: []} |
1164 | for license, licpath, _ in licvalues: | 1263 | for license, licpath, _ in licvalues: |
1264 | license = fixup_license(license) | ||
1165 | for pkgname, pkgpath in packages.items(): | 1265 | for pkgname, pkgpath in packages.items(): |
1166 | if licpath.startswith(pkgpath + '/'): | 1266 | if licpath.startswith(pkgpath + '/'): |
1167 | if pkgname in pkglicenses: | 1267 | if pkgname in pkglicenses: |
@@ -1174,11 +1274,14 @@ def split_pkg_licenses(licvalues, packages, outlines, fallback_licenses=None, pn | |||
1174 | pkglicenses[pn].append(license) | 1274 | pkglicenses[pn].append(license) |
1175 | outlicenses = {} | 1275 | outlicenses = {} |
1176 | for pkgname in packages: | 1276 | for pkgname in packages: |
1177 | license = ' '.join(list(set(pkglicenses.get(pkgname, ['Unknown'])))) or 'Unknown' | 1277 | # Assume AND operator between license files |
1178 | if license == 'Unknown' and pkgname in fallback_licenses: | 1278 | license = ' & '.join(list(set(pkglicenses.get(pkgname, ['Unknown'])))) or 'Unknown' |
1279 | if license == 'Unknown' and fallback_licenses and pkgname in fallback_licenses: | ||
1179 | license = fallback_licenses[pkgname] | 1280 | license = fallback_licenses[pkgname] |
1180 | outlines.append('LICENSE_%s = "%s"' % (pkgname, license)) | 1281 | licenses = tidy_licenses(license) |
1181 | outlicenses[pkgname] = license.split() | 1282 | license = ' & '.join(licenses) |
1283 | outlines.append('LICENSE:%s = "%s"' % (pkgname, license)) | ||
1284 | outlicenses[pkgname] = licenses | ||
1182 | return outlicenses | 1285 | return outlicenses |
1183 | 1286 | ||
1184 | def read_pkgconfig_provides(d): | 1287 | def read_pkgconfig_provides(d): |
@@ -1311,6 +1414,7 @@ def register_commands(subparsers): | |||
1311 | parser_create.add_argument('-B', '--srcbranch', help='Branch in source repository if fetching from an SCM such as git (default master)') | 1414 | parser_create.add_argument('-B', '--srcbranch', help='Branch in source repository if fetching from an SCM such as git (default master)') |
1312 | parser_create.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)') | 1415 | parser_create.add_argument('--keep-temp', action="store_true", help='Keep temporary directory (for debugging)') |
1313 | parser_create.add_argument('--npm-dev', action="store_true", help='For npm, also fetch devDependencies') | 1416 | parser_create.add_argument('--npm-dev', action="store_true", help='For npm, also fetch devDependencies') |
1417 | parser_create.add_argument('--no-pypi', action="store_true", help='Do not inherit pypi class') | ||
1314 | parser_create.add_argument('--devtool', action="store_true", help=argparse.SUPPRESS) | 1418 | parser_create.add_argument('--devtool', action="store_true", help=argparse.SUPPRESS) |
1315 | parser_create.add_argument('--mirrors', action="store_true", help='Enable PREMIRRORS and MIRRORS for source tree fetching (disabled by default).') | 1419 | parser_create.add_argument('--mirrors', action="store_true", help='Enable PREMIRRORS and MIRRORS for source tree fetching (disabled by default).') |
1316 | parser_create.set_defaults(func=create_recipe) | 1420 | parser_create.set_defaults(func=create_recipe) |