-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1943612 - Provide generic machinery to load confvars.sh as a simp…
…le key/value format r=glandium Doing so prevents users from putting customization à la moz.configure in the confvars.sh file, which should enforce better practices. Share the implementation with repackaging/msix.py and test it. Differential Revision: https://phabricator.services.mozilla.com/D235769
- Loading branch information
1 parent
1e2997d
commit bcdee5b
Showing
10 changed files
with
326 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||
# You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
import mozbuild.shellutil | ||
|
||
|
||
class ConfVarsSyntaxError(SyntaxError): | ||
def __init__(self, msg, file, lineno, colnum, line): | ||
super().__init__(msg, (file, lineno, colnum, line)) | ||
|
||
|
||
def parse(path): | ||
with open(path) as confvars: | ||
keyvals = {} | ||
for lineno, rawline in enumerate(confvars, start=1): | ||
line = rawline.rstrip() | ||
# Empty line / comment. | ||
line_no_leading_blank = line.lstrip() | ||
if not line_no_leading_blank or line_no_leading_blank.startswith("#"): | ||
continue | ||
|
||
head, sym, tail = line.partition("=") | ||
if sym != "=" or "#" in head: | ||
raise ConfVarsSyntaxError( | ||
"Expecting key=value format", path, lineno, 1, line | ||
) | ||
key = head.strip() | ||
|
||
# Verify there's no unexpected spaces. | ||
if key != head: | ||
colno = 1 + line.index(key) | ||
raise ConfVarsSyntaxError( | ||
f"Expecting no spaces around '{key}'", path, lineno, colno, line | ||
) | ||
if tail.lstrip() != tail: | ||
colno = 1 + line.index(tail) | ||
raise ConfVarsSyntaxError( | ||
f"Expecting no spaces between '=' and '{tail.lstrip()}'", | ||
path, | ||
lineno, | ||
colno, | ||
line, | ||
) | ||
|
||
# Verify we don't have duplicate keys. | ||
if key in keyvals: | ||
raise ConfVarsSyntaxError( | ||
f"Invalid redefinition for '{key}'", | ||
path, | ||
lineno, | ||
1 + line.index(key), | ||
line, | ||
) | ||
|
||
# Parse value. | ||
try: | ||
values = mozbuild.shellutil.split(tail) | ||
except mozbuild.shellutil.MetaCharacterException as e: | ||
raise ConfVarsSyntaxError( | ||
f"Unquoted, non-escaped special character '{e.char}'", | ||
path, | ||
lineno, | ||
1 + line.index(e.char), | ||
line, | ||
) | ||
except Exception as e: | ||
raise ConfVarsSyntaxError( | ||
e.args[0].replace(" in command", ""), | ||
path, | ||
lineno, | ||
1 + line.index("="), | ||
line, | ||
) | ||
value = values[0] if values else "" | ||
|
||
# Finally, commit the key<> value pair \o/. | ||
keyvals[key] = value | ||
return keyvals |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
python/mozbuild/mozbuild/test/configure/data/confvars/confvars.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# line comment | ||
CONFVAR=" a b c" | ||
OTHER_CONFVAR=d # trailing comment | ||
|
10 changes: 10 additions & 0 deletions
10
python/mozbuild/mozbuild/test/configure/data/confvars/moz.configure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
confvar( | ||
"CONFVAR", | ||
nargs=1, | ||
help="Confvar", | ||
) | ||
confvar( | ||
"OTHER_CONFVAR", | ||
nargs=1, | ||
help="Other confvar", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import os | ||
import re | ||
import unittest | ||
from tempfile import NamedTemporaryFile | ||
|
||
import mozunit | ||
|
||
from mozbuild.configure.confvars import ConfVarsSyntaxError, parse | ||
|
||
|
||
def TemporaryConfVars(): | ||
return NamedTemporaryFile("wt", delete=False) | ||
|
||
|
||
class TestContext(unittest.TestCase): | ||
|
||
def loads(self, *lines): | ||
with NamedTemporaryFile("wt", delete=False) as ntf: | ||
ntf.writelines(lines) | ||
try: | ||
confvars = parse(ntf.name) | ||
finally: | ||
os.remove(ntf.name) | ||
return confvars | ||
|
||
def test_parse_empty_file(self): | ||
confvars = self.loads("# comment\n") | ||
self.assertEqual(confvars, {}) | ||
|
||
def test_parse_simple_assignment(self): | ||
confvars = self.loads("a=b\n") | ||
self.assertEqual(confvars, {"a": "b"}) | ||
|
||
def test_parse_simple_assignment_with_equal_in_value(self): | ||
confvars = self.loads("a='='\n", "b==") | ||
self.assertEqual(confvars, {"a": "=", "b": "="}) | ||
|
||
def test_parse_simple_assignment_with_sharp_in_value(self): | ||
confvars = self.loads("a='#'\n") | ||
self.assertEqual(confvars, {"a": "#"}) | ||
|
||
def test_parse_simple_assignment_with_trailing_spaces(self): | ||
confvars = self.loads("a1=1\t\n", "\n", "a2=2\n", "a3=3 \n", "a4=4") | ||
|
||
self.assertEqual( | ||
confvars, | ||
{ | ||
"a1": "1", | ||
"a2": "2", | ||
"a3": "3", | ||
"a4": "4", | ||
}, | ||
) | ||
|
||
def test_parse_trailing_comment(self): | ||
confvars = self.loads("a=b#comment\n") | ||
self.assertEqual(confvars, {"a": "b"}) | ||
|
||
def test_parse_invalid_assign_in_trailing_comment(self): | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("a#=comment\n") | ||
self.assertTrue( | ||
re.match("Expecting key=value format \\(.*, line 1\\)", str(cm.exception)) | ||
) | ||
|
||
def test_parse_quoted_assignment(self): | ||
confvars = self.loads("a='b'\n" "b=' c'\n" 'c=" \'c"\n') | ||
self.assertEqual(confvars, {"a": "b", "b": " c", "c": " 'c"}) | ||
|
||
def test_parse_invalid_assignment(self): | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("a#comment\n") | ||
self.assertTrue( | ||
re.match("Expecting key=value format \\(.*, line 1\\)", str(cm.exception)) | ||
) | ||
|
||
def test_parse_empty_value(self): | ||
confvars = self.loads("a=\n") | ||
self.assertEqual(confvars, {"a": ""}) | ||
|
||
def test_parse_invalid_value(self): | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("#comment\na='er\n") | ||
self.assertTrue( | ||
re.match( | ||
"Unterminated quoted string \\(.*, line 2\\)", | ||
str(cm.exception), | ||
) | ||
) | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("a= er\n") | ||
self.assertTrue( | ||
re.match( | ||
"Expecting no spaces between '=' and 'er' \\(.*, line 1\\)", | ||
str(cm.exception), | ||
) | ||
) | ||
|
||
def test_parse_invalid_char(self): | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("a=$\n") | ||
self.assertTrue( | ||
re.match( | ||
"Unquoted, non-escaped special character '\\$' \\(.*, line 1\\)", | ||
str(cm.exception), | ||
) | ||
) | ||
|
||
def test_parse_invalid_key(self): | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads(" a=1\n") | ||
self.assertTrue( | ||
re.match( | ||
"Expecting no spaces around 'a' \\(.*, line 1\\)", | ||
str(cm.exception), | ||
) | ||
) | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("a =1\n") | ||
self.assertTrue( | ||
re.match( | ||
"Expecting no spaces around 'a' \\(.*, line 1\\)", | ||
str(cm.exception), | ||
) | ||
) | ||
|
||
def test_parse_redundant_key(self): | ||
with self.assertRaises(ConfVarsSyntaxError) as cm: | ||
self.loads("a=1\na=2\n") | ||
self.assertTrue( | ||
re.match( | ||
"Invalid redefinition for 'a' \\(.*, line 2\\)", | ||
str(cm.exception), | ||
) | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
mozunit.main() |
Oops, something went wrong.