Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 2 | # |
| 3 | # Copyright (C) 2009 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | import sys |
| 18 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 19 | # Usage: post_process_props.py file.prop [disallowed_key, ...] |
| 20 | # Disallowed keys are removed from the property file, if present |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 21 | |
Elliott Hughes | 05c1a2a | 2017-02-28 10:04:23 -0800 | [diff] [blame] | 22 | # See PROP_VALUE_MAX in system_properties.h. |
| 23 | # The constant in system_properties.h includes the terminating NUL, |
| 24 | # so we decrease the value by 1 here. |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 25 | PROP_VALUE_MAX = 91 |
| 26 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 27 | # Put the modifications that you need to make into the */build.prop into this |
| 28 | # function. |
| 29 | def mangle_build_prop(prop_list): |
Jerry Zhang | 1695653 | 2016-10-18 00:01:27 +0000 | [diff] [blame] | 30 | # If ro.debuggable is 1, then enable adb on USB by default |
| 31 | # (this is for userdebug builds) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 32 | if prop_list.get_value("ro.debuggable") == "1": |
| 33 | val = prop_list.get_value("persist.sys.usb.config") |
Jerry Zhang | 1695653 | 2016-10-18 00:01:27 +0000 | [diff] [blame] | 34 | if "adb" not in val: |
| 35 | if val == "": |
| 36 | val = "adb" |
| 37 | else: |
| 38 | val = val + ",adb" |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 39 | prop_list.put("persist.sys.usb.config", val) |
Joe Onorato | 8ad4bb1 | 2012-05-02 14:36:57 -0700 | [diff] [blame] | 40 | # UsbDeviceManager expects a value here. If it doesn't get it, it will |
| 41 | # default to "adb". That might not the right policy there, but it's better |
| 42 | # to be explicit. |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 43 | if not prop_list.get_value("persist.sys.usb.config"): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 44 | prop_list.put("persist.sys.usb.config", "none"); |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 45 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 46 | def validate(prop_list): |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 47 | """Validate the properties. |
| 48 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 49 | If the value of a sysprop exceeds the max limit (91), it's an error, unless |
| 50 | the sysprop is a read-only one. |
| 51 | |
| 52 | Checks if there is no optional prop assignments. |
| 53 | |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 54 | Returns: |
| 55 | True if nothing is wrong. |
| 56 | """ |
| 57 | check_pass = True |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 58 | for p in prop_list.get_all_props(): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 59 | if len(p.value) > PROP_VALUE_MAX and not p.name.startswith("ro."): |
Ying Wang | 38df101 | 2015-02-04 15:10:59 -0800 | [diff] [blame] | 60 | check_pass = False |
| 61 | sys.stderr.write("error: %s cannot exceed %d bytes: " % |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 62 | (p.name, PROP_VALUE_MAX)) |
| 63 | sys.stderr.write("%s (%d)\n" % (p.value, len(p.value))) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 64 | |
| 65 | if p.is_optional(): |
| 66 | check_pass = False |
| 67 | sys.stderr.write("error: found unresolved optional prop assignment:\n") |
| 68 | sys.stderr.write(str(p) + "\n") |
| 69 | |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 70 | return check_pass |
| 71 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 72 | def override_optional_props(prop_list): |
| 73 | """Override a?=b with a=c, if the latter exists |
| 74 | |
| 75 | Overriding is done by deleting a?=b |
| 76 | When there are a?=b and a?=c, then only the last one survives |
| 77 | When there are a=b and a=c, then it's an error. |
| 78 | |
| 79 | Returns: |
| 80 | True if the override was successful |
| 81 | """ |
| 82 | success = True |
| 83 | for name in prop_list.get_all_names(): |
| 84 | props = prop_list.get_props(name) |
| 85 | optional_props = [p for p in props if p.is_optional()] |
| 86 | overriding_props = [p for p in props if not p.is_optional()] |
| 87 | if len(overriding_props) > 1: |
| 88 | # duplicated props are allowed when the all have the same value |
| 89 | if all(overriding_props[0].value == p.value for p in overriding_props): |
| 90 | continue |
| 91 | success = False |
| 92 | sys.stderr.write("error: found duplicate sysprop assignments:\n") |
| 93 | for p in overriding_props: |
| 94 | sys.stderr.write("%s\n" % str(p)) |
| 95 | elif len(overriding_props) == 1: |
| 96 | for p in optional_props: |
| 97 | p.delete("overridden by %s" % str(overriding_props[0])) |
| 98 | else: |
| 99 | if len(optional_props) > 1: |
| 100 | for p in optional_props[:-1]: |
| 101 | p.delete("overridden by %s" % str(optional_props[-1])) |
| 102 | # Make the last optional one as non-optional |
| 103 | optional_props[-1].optional = False |
| 104 | |
| 105 | return success |
| 106 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 107 | class Prop: |
Yu Liu | 115c66b | 2014-02-10 19:20:36 -0800 | [diff] [blame] | 108 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 109 | def __init__(self, name, value, optional=False, comment=None): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 110 | self.name = name.strip() |
| 111 | self.value = value.strip() |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 112 | if comment != None: |
| 113 | self.comments = [comment] |
| 114 | else: |
| 115 | self.comments = [] |
| 116 | self.optional = optional |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 117 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 118 | @staticmethod |
| 119 | def from_line(line): |
| 120 | line = line.rstrip('\n') |
| 121 | if line.startswith("#"): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 122 | return Prop("", "", comment=line) |
| 123 | elif "?=" in line: |
| 124 | name, value = line.split("?=", 1) |
| 125 | return Prop(name, value, optional=True) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 126 | elif "=" in line: |
| 127 | name, value = line.split("=", 1) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 128 | return Prop(name, value, optional=False) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 129 | else: |
| 130 | # don't fail on invalid line |
| 131 | # TODO(jiyong) make this a hard error |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 132 | return Prop("", "", comment=line) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 133 | |
| 134 | def is_comment(self): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 135 | return bool(self.comments and not self.name) |
| 136 | |
| 137 | def is_optional(self): |
| 138 | return (not self.is_comment()) and self.optional |
| 139 | |
| 140 | def make_as_comment(self): |
| 141 | # Prepend "#" to the last line which is the prop assignment |
| 142 | if not self.is_comment(): |
| 143 | assignment = str(self).rsplit("\n", 1)[-1] |
| 144 | self.comments.append("#" + assignment) |
| 145 | self.name = "" |
| 146 | self.value = "" |
| 147 | |
| 148 | def delete(self, reason): |
| 149 | self.comments.append("# Removed by post_process_props.py because " + reason) |
| 150 | self.make_as_comment() |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 151 | |
| 152 | def __str__(self): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 153 | assignment = [] |
| 154 | if not self.is_comment(): |
| 155 | operator = "?=" if self.is_optional() else "=" |
| 156 | assignment.append(self.name + operator + self.value) |
| 157 | return "\n".join(self.comments + assignment) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 158 | |
| 159 | class PropList: |
| 160 | |
| 161 | def __init__(self, filename): |
| 162 | with open(filename) as f: |
| 163 | self.props = [Prop.from_line(l) |
| 164 | for l in f.readlines() if l.strip() != ""] |
| 165 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 166 | def get_all_props(self): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 167 | return [p for p in self.props if not p.is_comment()] |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 168 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 169 | def get_all_names(self): |
| 170 | return set([p.name for p in self.get_all_props()]) |
| 171 | |
| 172 | def get_props(self, name): |
| 173 | return [p for p in self.get_all_props() if p.name == name] |
| 174 | |
| 175 | def get_value(self, name): |
| 176 | # Caution: only the value of the first sysprop having the name is returned. |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 177 | return next((p.value for p in self.props if p.name == name), "") |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 178 | |
| 179 | def put(self, name, value): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 180 | # Note: when there is an optional prop for the name, its value isn't changed. |
| 181 | # Instead a new non-optional prop is appended, which will override the |
| 182 | # optional prop. Otherwise, the new value might be overridden by an existing |
| 183 | # non-optional prop of the same name. |
| 184 | index = next((i for i,p in enumerate(self.props) |
| 185 | if p.name == name and not p.is_optional()), -1) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 186 | if index == -1: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 187 | self.props.append(Prop(name, value, |
| 188 | comment="# Auto-added by post_process_props.py")) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 189 | else: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 190 | self.props[index].comments.append( |
| 191 | "# Value overridden by post_process_props.py. Original value: %s" % |
| 192 | self.props[index].value) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 193 | self.props[index].value = value |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 194 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 195 | def write(self, filename): |
| 196 | with open(filename, 'w+') as f: |
| 197 | for p in self.props: |
| 198 | f.write(str(p) + "\n") |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 199 | |
| 200 | def main(argv): |
| 201 | filename = argv[1] |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 202 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 203 | if not filename.endswith("/build.prop"): |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 204 | sys.stderr.write("bad command line: " + str(argv) + "\n") |
| 205 | sys.exit(1) |
| 206 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 207 | props = PropList(filename) |
| 208 | mangle_build_prop(props) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 209 | if not override_optional_props(props): |
| 210 | sys.exit(1) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 211 | if not validate(props): |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 212 | sys.exit(1) |
| 213 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 214 | # Drop any disallowed keys |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 215 | for key in argv[2:]: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 216 | for p in props.get_props(key): |
| 217 | p.delete("%s is a disallowed key" % key) |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 218 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 219 | props.write(filename) |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 220 | |
| 221 | if __name__ == "__main__": |
| 222 | main(sys.argv) |