Skip to content

Commit 3d49a59

Browse files
committed
Use latest API for bitbake parsing #1243
Signed-off-by: Philippe Ombredanne <[email protected]>
1 parent 1f51593 commit 3d49a59

File tree

6 files changed

+322
-240
lines changed

6 files changed

+322
-240
lines changed

src/packagedcode/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757

5858
bower.BowerJsonHandler,
5959

60-
bitbake.BitbakePackage,
60+
bitbake.BitbakeBbManifestHandler,
6161

6262
build_gradle.BuildGradleHandler,
6363

src/packagedcode/bitbake.py

Lines changed: 163 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,21 @@
11
#
2-
# Copyright (c) 2020 nexB Inc. and others. All rights reserved.
3-
# http://nexb.com and https://github.com/nexB/scancode-toolkit/
4-
# The ScanCode software is licensed under the Apache License version 2.0.
5-
# Data generated with ScanCode require an acknowledgment.
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
63
# ScanCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: Apache-2.0
5+
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
6+
# See https://github.com/nexB/scancode-toolkit for support or download.
7+
# See https://aboutcode.org for more information about nexB OSS projects.
78
#
8-
# You may not use this software except in compliance with the License.
9-
# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0
10-
# Unless required by applicable law or agreed to in writing, software distributed
11-
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12-
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
13-
# specific language governing permissions and limitations under the License.
14-
#
15-
# When you publish or redistribute any data created with ScanCode or any ScanCode
16-
# derivative work, you must accompany this data with the following acknowledgment:
17-
#
18-
# Generated with ScanCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES
19-
# OR CONDITIONS OF ANY KIND, either express or implied. No content created from
20-
# ScanCode should be considered or used as legal advice. Consult an Attorney
21-
# for any legal advice.
22-
# ScanCode is a free software code scanning tool from nexB Inc. and others.
23-
# Visit https://github.com/nexB/scancode-toolkit/ for support and download.
24-
25-
from __future__ import absolute_import
26-
from __future__ import print_function
27-
from __future__ import unicode_literals
289

2910
import logging
11+
import re
3012

31-
import attr
13+
from oelint_parser.cls_stash import Stash
14+
from packageurl import PackageURL
3215

33-
from commoncode import filetype
3416
from packagedcode import models
3517

36-
from oelint_parser.cls_stash import Stash
37-
from oelint_parser.cls_item import Variable
38-
39-
TRACE = False
18+
TRACE = True
4019

4120
logger = logging.getLogger(__name__)
4221

@@ -45,125 +24,169 @@
4524
logging.basicConfig(stream=sys.stdout)
4625
logger.setLevel(logging.DEBUG)
4726

48-
@attr.s()
49-
class BitbakePackage(models.Package):
50-
metafiles = ('*.bb',)
51-
default_type = 'bitbake'
5227

53-
@classmethod
54-
def recognize(cls, location):
55-
yield parse(location)
28+
class BitbakeBbManifestHandler(models.DatafileHandler):
29+
datasource_id = 'bitbake_bb_recipe'
30+
# note that there are .bbappend, .bbclass and bitbake.conf files.
31+
path_patterns = ('*.bb',)
32+
default_package_type = 'bitbake'
33+
description = 'BitBake bb recipe manifest'
34+
documentation_url = 'https://docs.yoctoproject.org/bitbake/bitbake-user-manual/bitbake-user-manual-metadata.html'
5635

5736
@classmethod
58-
def get_package_root(cls, manifest_resource, codebase):
59-
return manifest_resource.parent(codebase)
37+
def parse(cls, location):
38+
39+
oestash = Stash(quiet=True)
40+
41+
# add any bitbake like-file
42+
# TODO: may be we should handle the bbclass and bbappend here?
43+
oestash.AddFile(location)
44+
45+
# Resolves proper cross file dependencies
46+
oestash.Finalize()
47+
48+
# collect all variables of interest.
49+
# TODO: we should not get list values. Instead plain strings
50+
data = {
51+
k: ' '.join(v) if isinstance(v, (list, tuple)) else v
52+
for k, v in oestash.ExpandVar(filename=location).items()
53+
if v
54+
}
55+
name = data.get('PN')
56+
version = data.get('PV')
57+
description = data.get('DESCRIPTION')
58+
homepage_url = data.get('HOMEPAGE')
59+
download_url = data.get('PREMIRRORS')
60+
declared_license = data.get('LICENSE')
61+
62+
# The item.VarName for SRC_URI[*] from the parser are SRC_URI
63+
# Therefore, I cannot differentiate md5, sha1, or src file location reference
64+
# See: https://github.com/priv-kweihmann/oelint-parser/issues/3
65+
sha1 = data.get('SRC_URI[sha1sum]')
66+
md5 = data.get('SRC_URI[md5sum]')
67+
sha256 = data.get('SRC_URI[sha256sum]')
68+
sha512 = data.get('SRC_URI[sha512sum]')
69+
70+
dependencies = []
71+
72+
# Build deps: this is a list of plain BB recipes names
73+
# https://docs.yoctoproject.org/bitbake/bitbake-user-manual/bitbake-user-manual-ref-variables.html#term-DEPENDS
74+
build_deps = data.get('DEPENDS', '').split()
75+
for build_dep in build_deps:
76+
dep_purl = PackageURL(
77+
type=cls.default_package_type,
78+
name=build_dep,
79+
).to_string()
80+
81+
dependency = models.DependentPackage(
82+
purl=dep_purl,
83+
extracted_requirement=build_dep,
84+
scope='build',
85+
is_runtime=False,
86+
is_optional=True,
87+
is_resolved=False,
88+
)
89+
dependencies.append(dependency)
90+
91+
# Runtime deps:this is a list of Package names with an optional (=> 12) version constraint
92+
# https://docs.yoctoproject.org/bitbake/bitbake-user-manual/bitbake-user-manual-ref-variables.html#term-RDEPENDS
93+
# FIXME: There are some fields such as "RDEPENDS_${PN}" so these may not be correct in all cases
94+
for key, value in data.items():
95+
if not key.startswith('RDEPENDS'):
96+
continue
97+
if not value:
98+
continue
99+
for name, constraint in get_bitbake_deps(dependencies=value):
100+
if TRACE:
101+
logger.debug(f'RDEPENDS: name={name}, constraint={constraint}')
102+
dep_purl = PackageURL(
103+
type=cls.default_package_type,
104+
name=name,
105+
).to_string()
106+
107+
extracted_requirement = name
108+
if constraint:
109+
extracted_requirement += f' ({constraint})'
110+
111+
dependency = models.DependentPackage(
112+
purl=dep_purl,
113+
extracted_requirement=extracted_requirement,
114+
scope='install',
115+
is_runtime=True,
116+
is_optional=False,
117+
is_resolved=False,
118+
)
119+
120+
dependencies.append(dependency)
121+
122+
yield models.PackageData(
123+
datasource_id=cls.datasource_id,
124+
type=cls.default_package_type,
125+
name=name,
126+
version=version,
127+
description=description,
128+
homepage_url=homepage_url,
129+
download_url=download_url,
130+
sha1=sha1,
131+
md5=md5,
132+
sha256=sha256,
133+
sha512=sha512,
134+
declared_license=declared_license,
135+
dependencies=dependencies,
136+
)
60137

138+
@classmethod
139+
def assign_package_to_resources(cls, package, resource, codebase, package_adder):
140+
return models.DatafileHandler.assign_package_to_parent_tree(package, resource, codebase, package_adder)
61141

62-
def is_bb_file(location):
63-
return (filetype.is_file(location)
64-
and location.lower().endswith(('.bb',)))
65142

66-
def parse(location):
143+
def get_bitbake_deps(dependencies):
67144
"""
68-
Return a Package object from an ABOUT file or None.
145+
Return a list of tuple of (name, version constraint) given a BitBake
146+
dependencies string. "version constraint" can be None.
147+
148+
See https://docs.yoctoproject.org/ref-manual/variables.html?#term-RDEPENDS
149+
For example:
150+
>>> expected = [('ABC', None), ('abcd', '=>12312'), ('defg', None)]
151+
>>> result = get_bitbake_deps(" ABC abcd (= > 12312) defg ")
152+
>>> assert result == expected, result
153+
>>> expected = [('grub', '==12.23'), ('parted', None), ('e2fsprogs-mke2fs', None)]
154+
>>> result = get_bitbake_deps("grub (== 12.23) parted e2fsprogs-mke2fs")
155+
>>> assert result == expected, result
69156
"""
70-
if not is_bb_file(location):
71-
return
72-
73-
_stash = Stash()
74-
# add any bitbake like file
75-
_stash.AddFile(location)
76-
77-
# Resolves proper cross file dependencies
78-
_stash.Finalize()
79-
80-
# get all variables of the name PV from all files
81-
package_dict = {}
82-
for item in _stash.GetItemsFor():
83-
try:
84-
# Create a package dictionary with VarName as the key and
85-
# VarValueStripped as the value
86-
name = item.VarName
87-
value = item.VarValueStripped
88-
try:
89-
if package_dict[name]:
90-
package_dict[name] += '\n' + value
91-
except:
92-
package_dict[name] = value
93-
except:
94-
# Continue to see if there is any VarName value until the end of
95-
# the file
96-
continue
97-
98-
return build_package(package_dict)
99-
100-
101-
def build_package(package_dict):
157+
return [split_name_constraint(nc) for nc in split_deps(dependencies)]
158+
159+
160+
def split_name_constraint(dependency):
102161
"""
103-
Return a Package built from `package_dict` obtained from the .bb files.
162+
Return a tuple (name, version constraint) strings given a name (version
163+
constraint) BitBake dependency string.
164+
See https://docs.yoctoproject.org/ref-manual/variables.html?#term-RDEPENDS
165+
For example:
166+
>>> assert split_name_constraint(" abcd ( = 12312 ) ") == ("abcd", "=12312",)
167+
>>> assert split_name_constraint("abcd ") == ("abcd", None)
104168
"""
105-
# Initialization
106-
name = None
107-
version = None
108-
description = None
109-
homepage_url = None
110-
download_url = None
111-
sha1 = None
112-
md5 = None
113-
sha256 = None
114-
sha512 = None
115-
declared_license = None
116-
dependencies = None
117-
if 'PN' in package_dict:
118-
name = package_dict['PN']
119-
if 'PV' in package_dict:
120-
version = package_dict['PV']
121-
if 'DESCRIPTION' in package_dict:
122-
description = package_dict['DESCRIPTION']
123-
if 'HOMEPAGE' in package_dict:
124-
homepage_url = package_dict['HOMEPAGE']
125-
if 'PREMIRRORS' in package_dict:
126-
download_url = package_dict['PREMIRRORS']
127-
#The item.VarName for SRC_URI[*] from the parser are SRC_URI
128-
#Therefore, I cannot differentiate md5,sha1, or src file location reference
129-
# Entered an issue ticket: https://github.com/priv-kweihmann/oelint-parser/issues/3
169+
no_spaces = dependency.replace(' ', '')
170+
if '(' in no_spaces:
171+
name, _, constraint = no_spaces.partition('(')
172+
constraint = constraint.rstrip(')')
173+
return name, constraint
174+
return no_spaces, None
175+
176+
177+
def split_deps(dependencies):
130178
"""
131-
if 'SRC_URI[sha1sum]' in package_dict:
132-
sha1 = package_dict['SRC_URI[sha1sum]']
133-
if 'SRC_URI[md5sum]' in package_dict:
134-
md5 = package_dict['SRC_URI[md5sum]']
135-
if 'SRC_URI[sha256sum]' in package_dict:
136-
sha256 = package_dict['SRC_URI[sha256sum]']
137-
if 'SRC_URI[sha512sum]' in package_dict:
138-
sha512 = package_dict['SRC_URI[sha512sum]']
179+
Return a list of name (version constraint) strings given a BitBake
180+
dependencies string.
181+
182+
See https://docs.yoctoproject.org/ref-manual/variables.html?#term-RDEPENDS
183+
For example:
184+
>>> expected = ['ABC', 'abcd (= > 12312)', 'defg', 'foo', 'bar']
185+
>>> result = split_deps(" ABC abcd (= > 12312) defg foo bar ")
186+
>>> assert result == expected, result
139187
"""
140-
if 'LICENSE' in package_dict:
141-
declared_license = package_dict['LICENSE']
142-
if 'DEPENDS' in package_dict:
143-
if dependencies:
144-
dependencies += '\n' + package_dict['DEPENDS']
145-
else:
146-
dependencies = package_dict['DEPENDS']
147-
# There are some RDEPENDS_* fields such as "RDEPENDS_${PN}" which I need to
148-
# check with the substring
149-
for d in package_dict:
150-
if 'RDEPENDS' in d:
151-
if dependencies:
152-
dependencies += '\n' + package_dict[d]
153-
else:
154-
dependencies = package_dict[d]
155-
156-
return BitbakePackage(
157-
type='bitbake',
158-
name=name,
159-
version=version,
160-
description=description,
161-
homepage_url=homepage_url,
162-
download_url=download_url,
163-
sha1=sha1,
164-
md5=md5,
165-
sha256=sha256,
166-
sha512=sha512,
167-
declared_license=declared_license,
168-
dependencies=dependencies
169-
)
188+
normalized_spaces = ' '.join(dependencies.split())
189+
name = r'\w[\w\d_-]+'
190+
version_constraint = r'\([<>= ]+[^<>= ]+\s?\)'
191+
splitter = re.compile(fr'({name}\s?(?:{version_constraint})?)').findall
192+
return [s.strip() for s in splitter(normalized_spaces)]

tests/packagedcode/data/bitbake/initramfs-live-install-testfs_1.0.bb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ SUMMARY = "Live image install script with a second rootfs/kernel"
22
LICENSE = "MIT"
33
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
44
SRC_URI = "file://init-install-testfs.sh"
5+
DEPENDS = "do_this and that"
56

6-
RDEPENDS_${PN} = "grub parted e2fsprogs-mke2fs"
7+
RDEPENDS_${PN} = "grub (== 12.23) parted e2fsprogs-mke2fs"
78

89
S = "${WORKDIR}"
910

0 commit comments

Comments
 (0)