pmb/parse/_apkbuild: fix parsing commented lines (!1837)
Properly ignore comments at the end of lines, instead of assuming that all lines below belong to the attribute: subpackages="$pkgname-dev" # $pkgname-lang Fixes build.postmarketos.org#61, where pmbootstrap would assume that a random package provides "make", just because the word "make" is written somewhere below subpackages=" in the APKBUILD and it is parsed incorrectly. While at it, also support the ' character for quotations and detect if a quotation for a value was started, but there is no end quotation sign in the rest of the file. I've added tests, and manually checked that this did not introduce any parsing bugs for all the APKBUILDs in pmaports.git, by running 'pmbootstrap apkbuild_parse' with the old and new code, and diffing the result.
This commit is contained in:
parent
3587812539
commit
5438085e62
|
@ -122,6 +122,64 @@ def read_file(path):
|
|||
return lines
|
||||
|
||||
|
||||
def parse_attribute(attribute, lines, i, path):
|
||||
"""
|
||||
Parse one attribute from the APKBUILD.
|
||||
|
||||
It may be written across multiple lines, use a quoting sign and/or have
|
||||
a comment at the end. Some examples:
|
||||
|
||||
pkgrel=3
|
||||
options="!check" # ignore this comment
|
||||
arch='all !armhf'
|
||||
depends="
|
||||
first-pkg
|
||||
second-pkg"
|
||||
|
||||
:param attribute: from the APKBUILD, i.e. "pkgname"
|
||||
:param lines: \n-terminated list of lines from the APKBUILD
|
||||
:param i: index of the line we are currently looking at
|
||||
:param path: full path to the APKBUILD (for error message)
|
||||
:returns: (found, value, i)
|
||||
found: True if the attribute was found in line i, False otherwise
|
||||
value: that was parsed from the line
|
||||
i: line that was parsed last
|
||||
"""
|
||||
# Check for and cut off "attribute="
|
||||
if not lines[i].startswith(attribute + "="):
|
||||
return (False, None, i)
|
||||
value = lines[i][len(attribute + "="):-1]
|
||||
|
||||
# Determine end quote sign
|
||||
end_char = None
|
||||
for char in ["'", "\""]:
|
||||
if value.startswith(char):
|
||||
end_char = char
|
||||
value = value[1:]
|
||||
break
|
||||
|
||||
# Single line
|
||||
if not end_char:
|
||||
return (True, value, i)
|
||||
if end_char in value:
|
||||
value = value.split(end_char, 1)[0]
|
||||
return (True, value, i)
|
||||
|
||||
# Parse lines until reaching end quote
|
||||
i += 1
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
value += " "
|
||||
if end_char in line:
|
||||
value += line.split(end_char, 1)[0].strip()
|
||||
return (True, value.strip(), i)
|
||||
value += line.strip()
|
||||
i += 1
|
||||
|
||||
raise RuntimeError("Can't find closing quote sign (" + end_char + ") for"
|
||||
" attribute '" + attribute + "' in: " + path)
|
||||
|
||||
|
||||
def apkbuild(args, path, check_pkgver=True, check_pkgname=True):
|
||||
"""
|
||||
Parse relevant information out of the APKBUILD file. This is not meant
|
||||
|
@ -148,28 +206,10 @@ def apkbuild(args, path, check_pkgver=True, check_pkgname=True):
|
|||
ret = {}
|
||||
for i in range(len(lines)):
|
||||
for attribute, options in pmb.config.apkbuild_attributes.items():
|
||||
if not lines[i].startswith(attribute + "="):
|
||||
found, value, i = parse_attribute(attribute, lines, i, path)
|
||||
if not found:
|
||||
continue
|
||||
|
||||
# Extend the line value until we reach the ending quote sign
|
||||
line_value = lines[i][len(attribute + "="):-1]
|
||||
end_char = None
|
||||
if line_value.startswith("\""):
|
||||
end_char = "\""
|
||||
value = ""
|
||||
first_line = i
|
||||
while i < len(lines) - 1:
|
||||
value += line_value.replace("\"", "").strip()
|
||||
if not end_char:
|
||||
break
|
||||
elif line_value.endswith(end_char):
|
||||
# This check is needed to allow line break directly after opening quote
|
||||
if i != first_line or line_value.count(end_char) > 1:
|
||||
break
|
||||
value += " "
|
||||
i += 1
|
||||
line_value = lines[i][:-1]
|
||||
|
||||
# Support depends="$depends hello-world" (#1800)
|
||||
if attribute == "depends" and ("${depends}" in value or
|
||||
"$depends" in value):
|
||||
|
|
|
@ -78,3 +78,48 @@ def test_depends_in_depends(args):
|
|||
path = pmb_src + "/test/testdata/apkbuild/APKBUILD.depends-in-depends"
|
||||
apkbuild = pmb.parse.apkbuild(args, path, check_pkgname=False)
|
||||
assert apkbuild["depends"] == ["first", "second", "third"]
|
||||
|
||||
|
||||
def test_parse_attributes():
|
||||
# Convenience function for calling the function with a block of text
|
||||
def func(attribute, block):
|
||||
lines = block.split("\n")
|
||||
for i in range(0, len(lines)):
|
||||
lines[i] += "\n"
|
||||
i = 0
|
||||
path = "(testcase in " + __file__ + ")"
|
||||
print("=== parsing attribute '" + attribute + "' in test block:")
|
||||
print(block)
|
||||
print("===")
|
||||
return pmb.parse._apkbuild.parse_attribute(attribute, lines, i, path)
|
||||
|
||||
assert func("depends", "pkgname='test'") == (False, None, 0)
|
||||
|
||||
assert func("pkgname", 'pkgname="test"') == (True, "test", 0)
|
||||
|
||||
assert func("pkgname", "pkgname='test'") == (True, "test", 0)
|
||||
|
||||
assert func("pkgname", "pkgname=test") == (True, "test", 0)
|
||||
|
||||
assert func("pkgname", 'pkgname="test\n"') == (True, "test", 1)
|
||||
|
||||
assert func("pkgname", 'pkgname="\ntest\n"') == (True, "test", 2)
|
||||
|
||||
assert func("pkgname", 'pkgname="test" # random comment\npkgrel=3') == \
|
||||
(True, "test", 0)
|
||||
|
||||
assert func("depends", "depends='\nfirst\nsecond\nthird\n'#") == \
|
||||
(True, "first second third", 4)
|
||||
|
||||
assert func("depends", 'depends="\nfirst\n\tsecond third"') == \
|
||||
(True, "first second third", 2)
|
||||
|
||||
assert func("depends", 'depends=') == (True, "", 0)
|
||||
|
||||
with pytest.raises(RuntimeError) as e:
|
||||
func("depends", 'depends="\nmissing\nend\nquote\sign')
|
||||
assert str(e.value).startswith("Can't find closing")
|
||||
|
||||
with pytest.raises(RuntimeError) as e:
|
||||
func("depends", 'depends="')
|
||||
assert str(e.value).startswith("Can't find closing")
|
||||
|
|
Loading…
Reference in New Issue