android_build/tools/post_process_props.py
Justin Yun 48aa78ed3c Remove grf_required_api_level
As we don't fix the grf window, we may not calculate the grf
expiration date and the required api level.
The verification of this will be covered by the tests at run time.

Bug: 176950752
Test: atest --host post_process_props_unittest
Change-Id: I1205f0d9a9da5bc508a49acbcbb7da581800bf45
2021-04-13 17:58:59 +09:00

282 lines
9.4 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import sys
# Usage: post_process_props.py file.prop [disallowed_key, ...]
# Disallowed keys are removed from the property file, if present
# See PROP_VALUE_MAX in system_properties.h.
# The constant in system_properties.h includes the terminating NUL,
# so we decrease the value by 1 here.
PROP_VALUE_MAX = 91
# Put the modifications that you need to make into the */build.prop into this
# function.
def mangle_build_prop(prop_list):
# If ro.debuggable is 1, then enable adb on USB by default
# (this is for userdebug builds)
if prop_list.get_value("ro.debuggable") == "1":
val = prop_list.get_value("persist.sys.usb.config")
if "adb" not in val:
if val == "":
val = "adb"
else:
val = val + ",adb"
prop_list.put("persist.sys.usb.config", val)
# UsbDeviceManager expects a value here. If it doesn't get it, it will
# default to "adb". That might not the right policy there, but it's better
# to be explicit.
if not prop_list.get_value("persist.sys.usb.config"):
prop_list.put("persist.sys.usb.config", "none")
def validate_grf_props(prop_list, sdk_version):
"""Validate GRF properties if exist.
If ro.board.first_api_level is defined, check if its value is valid for the
sdk version.
Also, validate the value of ro.board.api_level if defined.
Returns:
True if the GRF properties are valid.
"""
grf_api_level = prop_list.get_value("ro.board.first_api_level")
board_api_level = prop_list.get_value("ro.board.api_level")
if not grf_api_level:
if board_api_level:
sys.stderr.write("error: non-GRF device must not define "
"ro.board.api_level\n")
return False
# non-GRF device skips the GRF validation test
return True
grf_api_level = int(grf_api_level)
if grf_api_level > sdk_version:
sys.stderr.write("error: ro.board.first_api_level(%d) must be less than "
"or equal to ro.build.version.sdk(%d)\n"
% (grf_api_level, sdk_version))
return False
if board_api_level:
board_api_level = int(board_api_level)
if board_api_level < grf_api_level or board_api_level > sdk_version:
sys.stderr.write("error: ro.board.api_level(%d) must be neither less "
"than ro.board.first_api_level(%d) nor greater than "
"ro.build.version.sdk(%d)\n"
% (board_api_level, grf_api_level, sdk_version))
return False
return True
def validate(prop_list):
"""Validate the properties.
If the value of a sysprop exceeds the max limit (91), it's an error, unless
the sysprop is a read-only one.
Checks if there is no optional prop assignments.
Returns:
True if nothing is wrong.
"""
check_pass = True
for p in prop_list.get_all_props():
if len(p.value) > PROP_VALUE_MAX and not p.name.startswith("ro."):
check_pass = False
sys.stderr.write("error: %s cannot exceed %d bytes: " %
(p.name, PROP_VALUE_MAX))
sys.stderr.write("%s (%d)\n" % (p.value, len(p.value)))
if p.is_optional():
check_pass = False
sys.stderr.write("error: found unresolved optional prop assignment:\n")
sys.stderr.write(str(p) + "\n")
return check_pass
def override_optional_props(prop_list, allow_dup=False):
"""Override a?=b with a=c, if the latter exists
Overriding is done by deleting a?=b
When there are a?=b and a?=c, then only the last one survives
When there are a=b and a=c, then it's an error.
Returns:
True if the override was successful
"""
success = True
for name in prop_list.get_all_names():
props = prop_list.get_props(name)
optional_props = [p for p in props if p.is_optional()]
overriding_props = [p for p in props if not p.is_optional()]
if len(overriding_props) > 1:
# duplicated props are allowed when the all have the same value
if all(overriding_props[0].value == p.value for p in overriding_props):
for p in optional_props:
p.delete("overridden by %s" % str(overriding_props[0]))
continue
# or if dup is explicitly allowed for compat reason
if allow_dup:
# this could left one or more optional props unresolved.
# Convert them into non-optional because init doesn't understand ?=
# syntax
for p in optional_props:
p.optional = False
continue
success = False
sys.stderr.write("error: found duplicate sysprop assignments:\n")
for p in overriding_props:
sys.stderr.write("%s\n" % str(p))
elif len(overriding_props) == 1:
for p in optional_props:
p.delete("overridden by %s" % str(overriding_props[0]))
else:
if len(optional_props) > 1:
for p in optional_props[:-1]:
p.delete("overridden by %s" % str(optional_props[-1]))
# Make the last optional one as non-optional
optional_props[-1].optional = False
return success
class Prop:
def __init__(self, name, value, optional=False, comment=None):
self.name = name.strip()
self.value = value.strip()
if comment != None:
self.comments = [comment]
else:
self.comments = []
self.optional = optional
@staticmethod
def from_line(line):
line = line.rstrip('\n')
if line.startswith("#"):
return Prop("", "", comment=line)
elif "?=" in line:
name, value = line.split("?=", 1)
return Prop(name, value, optional=True)
elif "=" in line:
name, value = line.split("=", 1)
return Prop(name, value, optional=False)
else:
# don't fail on invalid line
# TODO(jiyong) make this a hard error
return Prop("", "", comment=line)
def is_comment(self):
return bool(self.comments and not self.name)
def is_optional(self):
return (not self.is_comment()) and self.optional
def make_as_comment(self):
# Prepend "#" to the last line which is the prop assignment
if not self.is_comment():
assignment = str(self).rsplit("\n", 1)[-1]
self.comments.append("#" + assignment)
self.name = ""
self.value = ""
def delete(self, reason):
self.comments.append("# Removed by post_process_props.py because " + reason)
self.make_as_comment()
def __str__(self):
assignment = []
if not self.is_comment():
operator = "?=" if self.is_optional() else "="
assignment.append(self.name + operator + self.value)
return "\n".join(self.comments + assignment)
class PropList:
def __init__(self, filename):
with open(filename) as f:
self.props = [Prop.from_line(l)
for l in f.readlines() if l.strip() != ""]
def get_all_props(self):
return [p for p in self.props if not p.is_comment()]
def get_all_names(self):
return set([p.name for p in self.get_all_props()])
def get_props(self, name):
return [p for p in self.get_all_props() if p.name == name]
def get_value(self, name):
# Caution: only the value of the first sysprop having the name is returned.
return next((p.value for p in self.props if p.name == name), "")
def put(self, name, value):
# Note: when there is an optional prop for the name, its value isn't changed.
# Instead a new non-optional prop is appended, which will override the
# optional prop. Otherwise, the new value might be overridden by an existing
# non-optional prop of the same name.
index = next((i for i,p in enumerate(self.props)
if p.name == name and not p.is_optional()), -1)
if index == -1:
self.props.append(Prop(name, value,
comment="# Auto-added by post_process_props.py"))
else:
self.props[index].comments.append(
"# Value overridden by post_process_props.py. Original value: %s" %
self.props[index].value)
self.props[index].value = value
def write(self, filename):
with open(filename, 'w+') as f:
for p in self.props:
f.write(str(p) + "\n")
def main(argv):
parser = argparse.ArgumentParser(description="Post-process build.prop file")
parser.add_argument("--allow-dup", dest="allow_dup", action="store_true",
default=False)
parser.add_argument("filename")
parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*")
parser.add_argument("--sdk-version", type=int, required=True)
args = parser.parse_args()
if not args.filename.endswith("/build.prop"):
sys.stderr.write("bad command line: " + str(argv) + "\n")
sys.exit(1)
props = PropList(args.filename)
mangle_build_prop(props)
if not override_optional_props(props, args.allow_dup):
sys.exit(1)
if not validate_grf_props(props, args.sdk_version):
sys.exit(1)
if not validate(props):
sys.exit(1)
# Drop any disallowed keys
for key in args.disallowed_keys:
for p in props.get_props(key):
p.delete("%s is a disallowed key" % key)
props.write(args.filename)
if __name__ == "__main__":
main(sys.argv)