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 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 17 | import argparse |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 18 | import sys |
| 19 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 20 | # Usage: post_process_props.py file.prop [disallowed_key, ...] |
| 21 | # Disallowed keys are removed from the property file, if present |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 22 | |
Elliott Hughes | 05c1a2a | 2017-02-28 10:04:23 -0800 | [diff] [blame] | 23 | # See PROP_VALUE_MAX in system_properties.h. |
| 24 | # The constant in system_properties.h includes the terminating NUL, |
| 25 | # so we decrease the value by 1 here. |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 26 | PROP_VALUE_MAX = 91 |
| 27 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 28 | # Put the modifications that you need to make into the */build.prop into this |
| 29 | # function. |
| 30 | def mangle_build_prop(prop_list): |
Alexander Martinz | c4027a1 | 2023-09-28 20:24:00 +0200 | [diff] [blame] | 31 | # If ro.debuggable is 1, then enable adb on USB by default |
| 32 | # (this is for userdebug builds) |
| 33 | if prop_list.get_value("ro.debuggable") == "1": |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 34 | val = prop_list.get_value("persist.sys.usb.config") |
Jerry Zhang | 1695653 | 2016-10-18 00:01:27 +0000 | [diff] [blame] | 35 | if "adb" not in val: |
| 36 | if val == "": |
| 37 | val = "adb" |
| 38 | else: |
| 39 | val = val + ",adb" |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 40 | prop_list.put("persist.sys.usb.config", val) |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 41 | |
Justin Yun | 48aa78e | 2021-04-13 17:58:59 +0900 | [diff] [blame] | 42 | def validate_grf_props(prop_list, sdk_version): |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 43 | """Validate GRF properties if exist. |
| 44 | |
| 45 | If ro.board.first_api_level is defined, check if its value is valid for the |
| 46 | sdk version. |
Justin Yun | 48aa78e | 2021-04-13 17:58:59 +0900 | [diff] [blame] | 47 | Also, validate the value of ro.board.api_level if defined. |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 48 | |
| 49 | Returns: |
| 50 | True if the GRF properties are valid. |
| 51 | """ |
| 52 | grf_api_level = prop_list.get_value("ro.board.first_api_level") |
| 53 | board_api_level = prop_list.get_value("ro.board.api_level") |
| 54 | |
| 55 | if not grf_api_level: |
| 56 | if board_api_level: |
| 57 | sys.stderr.write("error: non-GRF device must not define " |
| 58 | "ro.board.api_level\n") |
| 59 | return False |
| 60 | # non-GRF device skips the GRF validation test |
| 61 | return True |
| 62 | |
| 63 | grf_api_level = int(grf_api_level) |
| 64 | if grf_api_level > sdk_version: |
| 65 | sys.stderr.write("error: ro.board.first_api_level(%d) must be less than " |
| 66 | "or equal to ro.build.version.sdk(%d)\n" |
| 67 | % (grf_api_level, sdk_version)) |
| 68 | return False |
| 69 | |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 70 | if board_api_level: |
| 71 | board_api_level = int(board_api_level) |
| 72 | if board_api_level < grf_api_level or board_api_level > sdk_version: |
| 73 | sys.stderr.write("error: ro.board.api_level(%d) must be neither less " |
| 74 | "than ro.board.first_api_level(%d) nor greater than " |
| 75 | "ro.build.version.sdk(%d)\n" |
| 76 | % (board_api_level, grf_api_level, sdk_version)) |
| 77 | return False |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 78 | |
| 79 | return True |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 80 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 81 | def validate(prop_list): |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 82 | """Validate the properties. |
| 83 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 84 | If the value of a sysprop exceeds the max limit (91), it's an error, unless |
| 85 | the sysprop is a read-only one. |
| 86 | |
| 87 | Checks if there is no optional prop assignments. |
| 88 | |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 89 | Returns: |
| 90 | True if nothing is wrong. |
| 91 | """ |
| 92 | check_pass = True |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 93 | for p in prop_list.get_all_props(): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 94 | 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] | 95 | check_pass = False |
| 96 | sys.stderr.write("error: %s cannot exceed %d bytes: " % |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 97 | (p.name, PROP_VALUE_MAX)) |
| 98 | sys.stderr.write("%s (%d)\n" % (p.value, len(p.value))) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 99 | |
| 100 | if p.is_optional(): |
| 101 | check_pass = False |
| 102 | sys.stderr.write("error: found unresolved optional prop assignment:\n") |
| 103 | sys.stderr.write(str(p) + "\n") |
| 104 | |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 105 | return check_pass |
| 106 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 107 | def override_optional_props(prop_list, allow_dup=False): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 108 | """Override a?=b with a=c, if the latter exists |
| 109 | |
| 110 | Overriding is done by deleting a?=b |
| 111 | When there are a?=b and a?=c, then only the last one survives |
| 112 | When there are a=b and a=c, then it's an error. |
| 113 | |
| 114 | Returns: |
| 115 | True if the override was successful |
| 116 | """ |
| 117 | success = True |
| 118 | for name in prop_list.get_all_names(): |
| 119 | props = prop_list.get_props(name) |
| 120 | optional_props = [p for p in props if p.is_optional()] |
| 121 | overriding_props = [p for p in props if not p.is_optional()] |
| 122 | if len(overriding_props) > 1: |
| 123 | # duplicated props are allowed when the all have the same value |
| 124 | if all(overriding_props[0].value == p.value for p in overriding_props): |
Jiyong Park | 24d9cad | 2020-06-30 11:41:23 +0900 | [diff] [blame] | 125 | for p in optional_props: |
| 126 | p.delete("overridden by %s" % str(overriding_props[0])) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 127 | continue |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 128 | # or if dup is explicitly allowed for compat reason |
| 129 | if allow_dup: |
| 130 | # this could left one or more optional props unresolved. |
| 131 | # Convert them into non-optional because init doesn't understand ?= |
| 132 | # syntax |
| 133 | for p in optional_props: |
| 134 | p.optional = False |
| 135 | continue |
| 136 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 137 | success = False |
| 138 | sys.stderr.write("error: found duplicate sysprop assignments:\n") |
| 139 | for p in overriding_props: |
| 140 | sys.stderr.write("%s\n" % str(p)) |
| 141 | elif len(overriding_props) == 1: |
| 142 | for p in optional_props: |
| 143 | p.delete("overridden by %s" % str(overriding_props[0])) |
| 144 | else: |
| 145 | if len(optional_props) > 1: |
| 146 | for p in optional_props[:-1]: |
| 147 | p.delete("overridden by %s" % str(optional_props[-1])) |
| 148 | # Make the last optional one as non-optional |
| 149 | optional_props[-1].optional = False |
| 150 | |
| 151 | return success |
| 152 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 153 | class Prop: |
Yu Liu | 115c66b | 2014-02-10 19:20:36 -0800 | [diff] [blame] | 154 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 155 | def __init__(self, name, value, optional=False, comment=None): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 156 | self.name = name.strip() |
| 157 | self.value = value.strip() |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 158 | if comment != None: |
| 159 | self.comments = [comment] |
| 160 | else: |
| 161 | self.comments = [] |
| 162 | self.optional = optional |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 163 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 164 | @staticmethod |
| 165 | def from_line(line): |
| 166 | line = line.rstrip('\n') |
| 167 | if line.startswith("#"): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 168 | return Prop("", "", comment=line) |
| 169 | elif "?=" in line: |
| 170 | name, value = line.split("?=", 1) |
| 171 | return Prop(name, value, optional=True) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 172 | elif "=" in line: |
| 173 | name, value = line.split("=", 1) |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 174 | return Prop(name, value, optional=False) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 175 | else: |
| 176 | # don't fail on invalid line |
| 177 | # TODO(jiyong) make this a hard error |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 178 | return Prop("", "", comment=line) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 179 | |
| 180 | def is_comment(self): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 181 | return bool(self.comments and not self.name) |
| 182 | |
| 183 | def is_optional(self): |
| 184 | return (not self.is_comment()) and self.optional |
| 185 | |
| 186 | def make_as_comment(self): |
| 187 | # Prepend "#" to the last line which is the prop assignment |
| 188 | if not self.is_comment(): |
| 189 | assignment = str(self).rsplit("\n", 1)[-1] |
| 190 | self.comments.append("#" + assignment) |
| 191 | self.name = "" |
| 192 | self.value = "" |
| 193 | |
| 194 | def delete(self, reason): |
| 195 | self.comments.append("# Removed by post_process_props.py because " + reason) |
| 196 | self.make_as_comment() |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 197 | |
| 198 | def __str__(self): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 199 | assignment = [] |
| 200 | if not self.is_comment(): |
| 201 | operator = "?=" if self.is_optional() else "=" |
| 202 | assignment.append(self.name + operator + self.value) |
| 203 | return "\n".join(self.comments + assignment) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 204 | |
| 205 | class PropList: |
| 206 | |
| 207 | def __init__(self, filename): |
| 208 | with open(filename) as f: |
| 209 | self.props = [Prop.from_line(l) |
| 210 | for l in f.readlines() if l.strip() != ""] |
| 211 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 212 | def get_all_props(self): |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 213 | return [p for p in self.props if not p.is_comment()] |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 214 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 215 | def get_all_names(self): |
| 216 | return set([p.name for p in self.get_all_props()]) |
| 217 | |
| 218 | def get_props(self, name): |
| 219 | return [p for p in self.get_all_props() if p.name == name] |
| 220 | |
| 221 | def get_value(self, name): |
| 222 | # 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] | 223 | 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] | 224 | |
| 225 | def put(self, name, value): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 226 | # Note: when there is an optional prop for the name, its value isn't changed. |
| 227 | # Instead a new non-optional prop is appended, which will override the |
| 228 | # optional prop. Otherwise, the new value might be overridden by an existing |
| 229 | # non-optional prop of the same name. |
| 230 | index = next((i for i,p in enumerate(self.props) |
| 231 | if p.name == name and not p.is_optional()), -1) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 232 | if index == -1: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 233 | self.props.append(Prop(name, value, |
| 234 | comment="# Auto-added by post_process_props.py")) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 235 | else: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 236 | self.props[index].comments.append( |
| 237 | "# Value overridden by post_process_props.py. Original value: %s" % |
| 238 | self.props[index].value) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 239 | self.props[index].value = value |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 240 | |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 241 | def write(self, filename): |
| 242 | with open(filename, 'w+') as f: |
| 243 | for p in self.props: |
| 244 | f.write(str(p) + "\n") |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 245 | |
| 246 | def main(argv): |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 247 | parser = argparse.ArgumentParser(description="Post-process build.prop file") |
| 248 | parser.add_argument("--allow-dup", dest="allow_dup", action="store_true", |
| 249 | default=False) |
| 250 | parser.add_argument("filename") |
| 251 | parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*") |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 252 | parser.add_argument("--sdk-version", type=int, required=True) |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 253 | args = parser.parse_args() |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 254 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 255 | if not args.filename.endswith("/build.prop"): |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 256 | sys.stderr.write("bad command line: " + str(argv) + "\n") |
| 257 | sys.exit(1) |
| 258 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 259 | props = PropList(args.filename) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 260 | mangle_build_prop(props) |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 261 | if not override_optional_props(props, args.allow_dup): |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 262 | sys.exit(1) |
Justin Yun | 48aa78e | 2021-04-13 17:58:59 +0900 | [diff] [blame] | 263 | if not validate_grf_props(props, args.sdk_version): |
Justin Yun | 07ceaa7 | 2021-04-02 16:29:06 +0900 | [diff] [blame] | 264 | sys.exit(1) |
Jiyong Park | ae55638 | 2020-05-20 18:33:43 +0900 | [diff] [blame] | 265 | if not validate(props): |
Ying Wang | 3512321 | 2014-02-11 20:44:09 -0800 | [diff] [blame] | 266 | sys.exit(1) |
| 267 | |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 268 | # Drop any disallowed keys |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 269 | for key in args.disallowed_keys: |
Jiyong Park | d721e87 | 2020-06-22 17:30:57 +0900 | [diff] [blame] | 270 | for p in props.get_props(key): |
| 271 | p.delete("%s is a disallowed key" % key) |
Jeff Sharkey | 26d22f7 | 2014-03-18 17:20:10 -0700 | [diff] [blame] | 272 | |
Jiyong Park | 0b4fccb | 2020-06-26 17:38:00 +0900 | [diff] [blame] | 273 | props.write(args.filename) |
Joe Onorato | 9197a48 | 2011-06-08 16:04:14 -0700 | [diff] [blame] | 274 | |
| 275 | if __name__ == "__main__": |
| 276 | main(sys.argv) |