summaryrefslogtreecommitdiffstats
path: root/meta/lib
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib')
-rw-r--r--meta/lib/oe/bootfiles.py2
-rw-r--r--meta/lib/oe/license_finder.py179
-rw-r--r--meta/lib/oe/package.py3
-rw-r--r--meta/lib/oe/packagedata.py3
-rw-r--r--meta/lib/oe/path.py3
-rw-r--r--meta/lib/oe/sstatesig.py2
-rw-r--r--meta/lib/oe/utils.py5
-rw-r--r--meta/lib/oeqa/selftest/cases/fitimage.py8
-rw-r--r--meta/lib/oeqa/selftest/cases/wic.py10
9 files changed, 204 insertions, 11 deletions
diff --git a/meta/lib/oe/bootfiles.py b/meta/lib/oe/bootfiles.py
index 155fe742db..7ee148c4e2 100644
--- a/meta/lib/oe/bootfiles.py
+++ b/meta/lib/oe/bootfiles.py
@@ -10,7 +10,7 @@
10# Returns a list of tuples with (original filepath relative to 10# Returns a list of tuples with (original filepath relative to
11# deploy_dir, desired filepath renaming) 11# deploy_dir, desired filepath renaming)
12# 12#
13# Heavily inspired of bootimg-partition.py 13# Heavily inspired of bootimg_partition.py
14# 14#
15def get_boot_files(deploy_dir, boot_files): 15def get_boot_files(deploy_dir, boot_files):
16 import re 16 import re
diff --git a/meta/lib/oe/license_finder.py b/meta/lib/oe/license_finder.py
new file mode 100644
index 0000000000..16f5d7c94c
--- /dev/null
+++ b/meta/lib/oe/license_finder.py
@@ -0,0 +1,179 @@
1#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6
7import fnmatch
8import hashlib
9import logging
10import os
11import re
12
13import bb
14import bb.utils
15
16logger = logging.getLogger("BitBake.OE.LicenseFinder")
17
18def _load_hash_csv(d):
19 """
20 Load a mapping of (checksum: license name) from all files/license-hashes.csv
21 files that can be found in the available layers.
22 """
23 import csv
24 md5sums = {}
25
26 # Read license md5sums from csv file
27 for path in d.getVar('BBPATH').split(':'):
28 csv_path = os.path.join(path, 'files', 'license-hashes.csv')
29 if os.path.isfile(csv_path):
30 with open(csv_path, newline='') as csv_file:
31 reader = csv.DictReader(csv_file, delimiter=',', fieldnames=['md5sum', 'license'])
32 for row in reader:
33 md5sums[row['md5sum']] = row['license']
34
35 return md5sums
36
37
38def _crunch_known_licenses(d):
39 """
40 Calculate the MD5 checksums for the original and "crunched" versions of all
41 known licenses.
42 """
43 md5sums = {}
44
45 lic_dirs = [d.getVar('COMMON_LICENSE_DIR')] + (d.getVar('LICENSE_PATH') or "").split()
46 for lic_dir in lic_dirs:
47 for fn in os.listdir(lic_dir):
48 path = os.path.join(lic_dir, fn)
49 # Hash the exact contents
50 md5value = bb.utils.md5_file(path)
51 md5sums[md5value] = fn
52 # Also hash a "crunched" version
53 md5value = _crunch_license(path)
54 md5sums[md5value] = fn
55
56 return md5sums
57
58
59def _crunch_license(licfile):
60 '''
61 Remove non-material text from a license file and then calculate its
62 md5sum. This works well for licenses that contain a copyright statement,
63 but is also a useful way to handle people's insistence upon reformatting
64 the license text slightly (with no material difference to the text of the
65 license).
66 '''
67
68 import oe.utils
69
70 # Note: these are carefully constructed!
71 license_title_re = re.compile(r'^#*\(? *(This is )?([Tt]he )?.{0,15} ?[Ll]icen[sc]e( \(.{1,10}\))?\)?[:\.]? ?#*$')
72 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:?$')
73 copyright_re = re.compile(r'^ *[#\*]* *(Modified work |MIT LICENSED )?Copyright ?(\([cC]\))? .*$')
74 disclaimer_re = re.compile(r'^ *\*? ?All [Rr]ights [Rr]eserved\.$')
75 email_re = re.compile(r'^.*<[\w\.-]*@[\w\.\-]*>$')
76 header_re = re.compile(r'^(\/\**!?)? ?[\-=\*]* ?(\*\/)?$')
77 tag_re = re.compile(r'^ *@?\(?([Ll]icense|MIT)\)?$')
78 url_re = re.compile(r'^ *[#\*]* *https?:\/\/[\w\.\/\-]+$')
79
80 lictext = []
81 with open(licfile, 'r', errors='surrogateescape') as f:
82 for line in f:
83 # Drop opening statements
84 if copyright_re.match(line):
85 continue
86 elif disclaimer_re.match(line):
87 continue
88 elif email_re.match(line):
89 continue
90 elif header_re.match(line):
91 continue
92 elif tag_re.match(line):
93 continue
94 elif url_re.match(line):
95 continue
96 elif license_title_re.match(line):
97 continue
98 elif license_statement_re.match(line):
99 continue
100 # Strip comment symbols
101 line = line.replace('*', '') \
102 .replace('#', '')
103 # Unify spelling
104 line = line.replace('sub-license', 'sublicense')
105 # Squash spaces
106 line = oe.utils.squashspaces(line.strip())
107 # Replace smart quotes, double quotes and backticks with single quotes
108 line = line.replace(u"\u2018", "'").replace(u"\u2019", "'").replace(u"\u201c","'").replace(u"\u201d", "'").replace('"', '\'').replace('`', '\'')
109 # Unify brackets
110 line = line.replace("{", "[").replace("}", "]")
111 if line:
112 lictext.append(line)
113
114 m = hashlib.md5()
115 try:
116 m.update(' '.join(lictext).encode('utf-8'))
117 md5val = m.hexdigest()
118 except UnicodeEncodeError:
119 md5val = None
120 return md5val
121
122
123def find_license_files(srctree, first_only=False):
124 """
125 Search srctree for files that look like they could be licenses.
126 If first_only is True, only return the first file found.
127 """
128 licspecs = ['*LICEN[CS]E*', 'COPYING*', '*[Ll]icense*', 'LEGAL*', '[Ll]egal*', '*GPL*', 'README.lic*', 'COPYRIGHT*', '[Cc]opyright*', 'e[dp]l-v10']
129 skip_extensions = (".html", ".js", ".json", ".svg", ".ts", ".go", ".sh")
130 licfiles = []
131 for root, dirs, files in os.walk(srctree):
132 # Sort files so that LICENSE is before LICENSE.subcomponent, which is
133 # meaningful if first_only is set.
134 for fn in sorted(files):
135 if fn.endswith(skip_extensions):
136 continue
137 for spec in licspecs:
138 if fnmatch.fnmatch(fn, spec):
139 fullpath = os.path.join(root, fn)
140 if not fullpath in licfiles:
141 licfiles.append(fullpath)
142 if first_only:
143 return licfiles
144
145 return licfiles
146
147
148def match_licenses(licfiles, srctree, d, extra_hashes={}):
149 md5sums = {}
150 md5sums.update(_load_hash_csv(d))
151 md5sums.update(_crunch_known_licenses(d))
152 md5sums.update(extra_hashes)
153
154 licenses = []
155 for licfile in sorted(licfiles):
156 resolved_licfile = d.expand(licfile)
157 md5value = bb.utils.md5_file(resolved_licfile)
158 license = md5sums.get(md5value, None)
159 if not license:
160 crunched_md5 = _crunch_license(resolved_licfile)
161 license = md5sums.get(crunched_md5, None)
162 if not license:
163 license = 'Unknown'
164 logger.info("Please add the following line for '%s' to a 'license-hashes.csv' " \
165 "and replace `Unknown` with the license:\n" \
166 "%s,Unknown" % (os.path.relpath(licfile, srctree + "/.."), md5value))
167
168 licenses.append((license, os.path.relpath(licfile, srctree), md5value))
169
170 return licenses
171
172
173def find_licenses(srctree, d, first_only=False, extra_hashes={}):
174 licfiles = find_license_files(srctree, first_only)
175 licenses = match_licenses(licfiles, srctree, d, extra_hashes)
176
177 # FIXME should we grab at least one source file with a license header and add that too?
178
179 return licenses
diff --git a/meta/lib/oe/package.py b/meta/lib/oe/package.py
index 60392cbced..ce69151e5d 100644
--- a/meta/lib/oe/package.py
+++ b/meta/lib/oe/package.py
@@ -16,6 +16,7 @@ import mmap
16import subprocess 16import subprocess
17import shutil 17import shutil
18 18
19import bb.parse
19import oe.cachedpath 20import oe.cachedpath
20 21
21def runstrip(file, elftype, strip, extra_strip_sections=''): 22def runstrip(file, elftype, strip, extra_strip_sections=''):
@@ -1049,6 +1050,7 @@ def copydebugsources(debugsrcdir, sources, d):
1049 if os.path.exists(p) and not os.listdir(p): 1050 if os.path.exists(p) and not os.listdir(p):
1050 os.rmdir(p) 1051 os.rmdir(p)
1051 1052
1053@bb.parse.vardepsexclude("BB_NUMBER_THREADS")
1052def save_debugsources_info(debugsrcdir, sources_raw, d): 1054def save_debugsources_info(debugsrcdir, sources_raw, d):
1053 import json 1055 import json
1054 import bb.compress.zstd 1056 import bb.compress.zstd
@@ -1081,6 +1083,7 @@ def save_debugsources_info(debugsrcdir, sources_raw, d):
1081 with bb.compress.zstd.open(debugsources_file, "wt", encoding="utf-8", num_threads=num_threads) as f: 1083 with bb.compress.zstd.open(debugsources_file, "wt", encoding="utf-8", num_threads=num_threads) as f:
1082 json.dump(sources_dict, f, sort_keys=True) 1084 json.dump(sources_dict, f, sort_keys=True)
1083 1085
1086@bb.parse.vardepsexclude("BB_NUMBER_THREADS")
1084def read_debugsources_info(d): 1087def read_debugsources_info(d):
1085 import json 1088 import json
1086 import bb.compress.zstd 1089 import bb.compress.zstd
diff --git a/meta/lib/oe/packagedata.py b/meta/lib/oe/packagedata.py
index 2d1d6ddeb7..b6a10a930a 100644
--- a/meta/lib/oe/packagedata.py
+++ b/meta/lib/oe/packagedata.py
@@ -7,6 +7,7 @@
7import codecs 7import codecs
8import os 8import os
9import json 9import json
10import bb.parse
10import bb.compress.zstd 11import bb.compress.zstd
11import oe.path 12import oe.path
12 13
@@ -64,6 +65,7 @@ def read_subpkgdata_dict(pkg, d):
64 ret[newvar] = subd[var] 65 ret[newvar] = subd[var]
65 return ret 66 return ret
66 67
68@bb.parse.vardepsexclude("BB_NUMBER_THREADS")
67def read_subpkgdata_extended(pkg, d): 69def read_subpkgdata_extended(pkg, d):
68 import json 70 import json
69 import bb.compress.zstd 71 import bb.compress.zstd
@@ -182,6 +184,7 @@ def runtime_mapping_rename(varname, pkg, d):
182 184
183 #bb.note("%s after: %s" % (varname, d.getVar(varname))) 185 #bb.note("%s after: %s" % (varname, d.getVar(varname)))
184 186
187@bb.parse.vardepsexclude("BB_NUMBER_THREADS")
185def emit_pkgdata(pkgfiles, d): 188def emit_pkgdata(pkgfiles, d):
186 def process_postinst_on_target(pkg, mlprefix): 189 def process_postinst_on_target(pkg, mlprefix):
187 pkgval = d.getVar('PKG:%s' % pkg) 190 pkgval = d.getVar('PKG:%s' % pkg)
diff --git a/meta/lib/oe/path.py b/meta/lib/oe/path.py
index 5d21cdcbdf..a1efe97d88 100644
--- a/meta/lib/oe/path.py
+++ b/meta/lib/oe/path.py
@@ -10,6 +10,8 @@ import shutil
10import subprocess 10import subprocess
11import os.path 11import os.path
12 12
13import bb.parse
14
13def join(*paths): 15def join(*paths):
14 """Like os.path.join but doesn't treat absolute RHS specially""" 16 """Like os.path.join but doesn't treat absolute RHS specially"""
15 return os.path.normpath("/".join(paths)) 17 return os.path.normpath("/".join(paths))
@@ -77,6 +79,7 @@ def replace_absolute_symlinks(basedir, d):
77 os.remove(path) 79 os.remove(path)
78 os.symlink(base, path) 80 os.symlink(base, path)
79 81
82@bb.parse.vardepsexclude("TOPDIR")
80def format_display(path, metadata): 83def format_display(path, metadata):
81 """ Prepare a path for display to the user. """ 84 """ Prepare a path for display to the user. """
82 rel = relative(metadata.getVar("TOPDIR"), path) 85 rel = relative(metadata.getVar("TOPDIR"), path)
diff --git a/meta/lib/oe/sstatesig.py b/meta/lib/oe/sstatesig.py
index 826549948e..ef687f5d41 100644
--- a/meta/lib/oe/sstatesig.py
+++ b/meta/lib/oe/sstatesig.py
@@ -3,6 +3,7 @@
3# 3#
4# SPDX-License-Identifier: GPL-2.0-only 4# SPDX-License-Identifier: GPL-2.0-only
5# 5#
6import bb.parse
6import bb.siggen 7import bb.siggen
7import bb.runqueue 8import bb.runqueue
8import oe 9import oe
@@ -493,6 +494,7 @@ def sstate_get_manifest_filename(task, d):
493 d2.setVar("SSTATE_MANMACH", extrainf) 494 d2.setVar("SSTATE_MANMACH", extrainf)
494 return (d2.expand("${SSTATE_MANFILEPREFIX}.%s" % task), d2) 495 return (d2.expand("${SSTATE_MANFILEPREFIX}.%s" % task), d2)
495 496
497@bb.parse.vardepsexclude("BBEXTENDCURR", "BBEXTENDVARIANT", "OVERRIDES", "PACKAGE_EXTRA_ARCHS")
496def find_sstate_manifest(taskdata, taskdata2, taskname, d, multilibcache): 498def find_sstate_manifest(taskdata, taskdata2, taskname, d, multilibcache):
497 d2 = d 499 d2 = d
498 variant = '' 500 variant = ''
diff --git a/meta/lib/oe/utils.py b/meta/lib/oe/utils.py
index d272dd2b8d..a11db5f3cd 100644
--- a/meta/lib/oe/utils.py
+++ b/meta/lib/oe/utils.py
@@ -9,6 +9,8 @@ import multiprocessing
9import traceback 9import traceback
10import errno 10import errno
11 11
12import bb.parse
13
12def read_file(filename): 14def read_file(filename):
13 try: 15 try:
14 f = open( filename, "r" ) 16 f = open( filename, "r" )
@@ -265,6 +267,7 @@ def execute_pre_post_process(d, cmds):
265 bb.note("Executing %s ..." % cmd) 267 bb.note("Executing %s ..." % cmd)
266 bb.build.exec_func(cmd, d) 268 bb.build.exec_func(cmd, d)
267 269
270@bb.parse.vardepsexclude("BB_NUMBER_THREADS")
268def get_bb_number_threads(d): 271def get_bb_number_threads(d):
269 return int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1) 272 return int(d.getVar("BB_NUMBER_THREADS") or os.cpu_count() or 1)
270 273
@@ -467,7 +470,7 @@ def host_gcc_version(d, taskcontextonly=False):
467 version = match.group(1) 470 version = match.group(1)
468 return "-%s" % version if version in ("4.8", "4.9") else "" 471 return "-%s" % version if version in ("4.8", "4.9") else ""
469 472
470 473@bb.parse.vardepsexclude("DEFAULTTUNE_MULTILIB_ORIGINAL", "OVERRIDES")
471def get_multilib_datastore(variant, d): 474def get_multilib_datastore(variant, d):
472 localdata = bb.data.createCopy(d) 475 localdata = bb.data.createCopy(d)
473 if variant: 476 if variant:
diff --git a/meta/lib/oeqa/selftest/cases/fitimage.py b/meta/lib/oeqa/selftest/cases/fitimage.py
index be291e4b0f..ca4724d1ae 100644
--- a/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -786,7 +786,7 @@ FIT_CONF_PREFIX = "foo-"
786 EXPECTED_COMP = ["ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"] 786 EXPECTED_COMP = ["ti,am335x-bone-green", "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx"]
787 787
788 config = """ 788 config = """
789DISTRO="poky" 789DISTRO = "poky"
790MACHINE = "beaglebone-yocto" 790MACHINE = "beaglebone-yocto"
791""" 791"""
792 self.write_config(config) 792 self.write_config(config)
@@ -918,7 +918,7 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
918 bb_vars = self._fit_get_bb_vars() 918 bb_vars = self._fit_get_bb_vars()
919 919
920 # Ensure new keys are generated and FIT_GENERATE_KEYS = "1" is tested 920 # Ensure new keys are generated and FIT_GENERATE_KEYS = "1" is tested
921 bitbake("kernel-signing-keys-native -c cleansstate") 921 bitbake("kernel-signing-keys-native -c compile -f")
922 922
923 self._test_fitimage(bb_vars) 923 self._test_fitimage(bb_vars)
924 924
@@ -938,7 +938,7 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
938 """ 938 """
939 939
940 config = """ 940 config = """
941DISTRO="poky" 941DISTRO = "poky"
942MACHINE = "beaglebone-yocto" 942MACHINE = "beaglebone-yocto"
943INITRAMFS_IMAGE = "core-image-minimal-initramfs" 943INITRAMFS_IMAGE = "core-image-minimal-initramfs"
944INITRAMFS_SCRIPTS = "" 944INITRAMFS_SCRIPTS = ""
@@ -992,7 +992,7 @@ FIT_HASH_ALG = "sha256"
992 """ 992 """
993 993
994 config = """ 994 config = """
995DISTRO="poky" 995DISTRO = "poky"
996MACHINE = "beaglebone-yocto" 996MACHINE = "beaglebone-yocto"
997INITRAMFS_IMAGE_BUNDLE = "1" 997INITRAMFS_IMAGE_BUNDLE = "1"
998INITRAMFS_IMAGE = "core-image-minimal-initramfs" 998INITRAMFS_IMAGE = "core-image-minimal-initramfs"
diff --git a/meta/lib/oeqa/selftest/cases/wic.py b/meta/lib/oeqa/selftest/cases/wic.py
index 1a67b6df51..f45608172f 100644
--- a/meta/lib/oeqa/selftest/cases/wic.py
+++ b/meta/lib/oeqa/selftest/cases/wic.py
@@ -153,7 +153,7 @@ class Wic(WicTestCase):
153 # create a temporary file for the WKS content 153 # create a temporary file for the WKS content
154 with NamedTemporaryFile("w", suffix=".wks") as wks: 154 with NamedTemporaryFile("w", suffix=".wks") as wks:
155 wks.write( 155 wks.write(
156 'part --source bootimg-efi ' 156 'part --source bootimg_efi '
157 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=false" ' 157 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=false" '
158 '--label boot --active\n' 158 '--label boot --active\n'
159 ) 159 )
@@ -186,7 +186,7 @@ class Wic(WicTestCase):
186 # create a temporary file for the WKS content 186 # create a temporary file for the WKS content
187 with NamedTemporaryFile("w", suffix=".wks") as wks: 187 with NamedTemporaryFile("w", suffix=".wks") as wks:
188 wks.write( 188 wks.write(
189 'part --source bootimg-efi ' 189 'part --source bootimg_efi '
190 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=true" ' 190 '--sourceparams="loader=grub-efi,install-kernel-into-boot-dir=true" '
191 '--label boot --active\n' 191 '--label boot --active\n'
192 ) 192 )
@@ -1358,7 +1358,7 @@ class Wic2(WicTestCase):
1358 def test_biosplusefi_plugin(self): 1358 def test_biosplusefi_plugin(self):
1359 """Test biosplusefi plugin""" 1359 """Test biosplusefi plugin"""
1360 # Wic generation below may fail depending on the order of the unittests 1360 # Wic generation below may fail depending on the order of the unittests
1361 # This is because bootimg-pcbios (that bootimg-biosplusefi uses) generate its MBR inside STAGING_DATADIR directory 1361 # This is because bootimg_pcbios (that bootimg_biosplusefi uses) generate its MBR inside STAGING_DATADIR directory
1362 # which may or may not exists depending on what was built already 1362 # which may or may not exists depending on what was built already
1363 # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir() 1363 # If an image hasn't been built yet, directory ${STAGING_DATADIR}/syslinux won't exists and _get_bootimg_dir()
1364 # will raise with "Couldn't find correct bootimg_dir" 1364 # will raise with "Couldn't find correct bootimg_dir"
@@ -1370,7 +1370,7 @@ class Wic2(WicTestCase):
1370 1370
1371 img = 'core-image-minimal' 1371 img = 'core-image-minimal'
1372 with NamedTemporaryFile("w", suffix=".wks") as wks: 1372 with NamedTemporaryFile("w", suffix=".wks") as wks:
1373 wks.writelines(['part /boot --active --source bootimg-biosplusefi --sourceparams="loader=grub-efi"\n', 1373 wks.writelines(['part /boot --active --source bootimg_biosplusefi --sourceparams="loader=grub-efi"\n',
1374 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ 1374 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1375 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) 1375 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1376 wks.flush() 1376 wks.flush()
@@ -1390,7 +1390,7 @@ class Wic2(WicTestCase):
1390 1390
1391 img = 'core-image-minimal' 1391 img = 'core-image-minimal'
1392 with NamedTemporaryFile("w", suffix=".wks") as wks: 1392 with NamedTemporaryFile("w", suffix=".wks") as wks:
1393 wks.writelines(['part /boot --source bootimg-efi --sourceparams="loader=uefi-kernel"\n' 1393 wks.writelines(['part /boot --source bootimg_efi --sourceparams="loader=uefi-kernel"\n'
1394 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\ 1394 'part / --source rootfs --fstype=ext4 --align 1024 --use-uuid\n'\
1395 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n']) 1395 'bootloader --timeout=0 --append="console=ttyS0,115200n8"\n'])
1396 wks.flush() 1396 wks.flush()