xref: /aosp_15_r20/build/make/tools/post_process_props.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*9e94795aSAndroid Build Coastguard Worker#
3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2009 The Android Open Source Project
4*9e94795aSAndroid Build Coastguard Worker#
5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*9e94795aSAndroid Build Coastguard Worker#
9*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*9e94795aSAndroid Build Coastguard Worker#
11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport argparse
18*9e94795aSAndroid Build Coastguard Workerimport sys
19*9e94795aSAndroid Build Coastguard Worker
20*9e94795aSAndroid Build Coastguard Workerfrom uffd_gc_utils import should_enable_uffd_gc
21*9e94795aSAndroid Build Coastguard Worker
22*9e94795aSAndroid Build Coastguard Worker# Usage: post_process_props.py file.prop [disallowed_key, ...]
23*9e94795aSAndroid Build Coastguard Worker# Disallowed keys are removed from the property file, if present
24*9e94795aSAndroid Build Coastguard Worker
25*9e94795aSAndroid Build Coastguard Worker# See PROP_VALUE_MAX in system_properties.h.
26*9e94795aSAndroid Build Coastguard Worker# The constant in system_properties.h includes the terminating NUL,
27*9e94795aSAndroid Build Coastguard Worker# so we decrease the value by 1 here.
28*9e94795aSAndroid Build Coastguard WorkerPROP_VALUE_MAX = 91
29*9e94795aSAndroid Build Coastguard Worker
30*9e94795aSAndroid Build Coastguard Worker# Put the modifications that you need to make into the */build.prop into this
31*9e94795aSAndroid Build Coastguard Worker# function.
32*9e94795aSAndroid Build Coastguard Workerdef mangle_build_prop(prop_list, kernel_version_file_for_uffd_gc):
33*9e94795aSAndroid Build Coastguard Worker  # If ro.debuggable is 1, then enable adb on USB by default
34*9e94795aSAndroid Build Coastguard Worker  # (this is for userdebug builds)
35*9e94795aSAndroid Build Coastguard Worker  if prop_list.get_value("ro.debuggable") == "1":
36*9e94795aSAndroid Build Coastguard Worker    val = prop_list.get_value("persist.sys.usb.config")
37*9e94795aSAndroid Build Coastguard Worker    if "adb" not in val:
38*9e94795aSAndroid Build Coastguard Worker      if val == "":
39*9e94795aSAndroid Build Coastguard Worker        val = "adb"
40*9e94795aSAndroid Build Coastguard Worker      else:
41*9e94795aSAndroid Build Coastguard Worker        val = val + ",adb"
42*9e94795aSAndroid Build Coastguard Worker      prop_list.put("persist.sys.usb.config", val)
43*9e94795aSAndroid Build Coastguard Worker  if prop_list.get_value("ro.dalvik.vm.enable_uffd_gc") == "default":
44*9e94795aSAndroid Build Coastguard Worker    assert kernel_version_file_for_uffd_gc != ""
45*9e94795aSAndroid Build Coastguard Worker    enable_uffd_gc = should_enable_uffd_gc(kernel_version_file_for_uffd_gc)
46*9e94795aSAndroid Build Coastguard Worker    prop_list.put("ro.dalvik.vm.enable_uffd_gc",
47*9e94795aSAndroid Build Coastguard Worker                  "true" if enable_uffd_gc else "false")
48*9e94795aSAndroid Build Coastguard Worker
49*9e94795aSAndroid Build Coastguard Workerdef validate_grf_props(prop_list):
50*9e94795aSAndroid Build Coastguard Worker  """Validate GRF properties if exist.
51*9e94795aSAndroid Build Coastguard Worker
52*9e94795aSAndroid Build Coastguard Worker  If ro.board.first_api_level is defined, check if its value is valid.
53*9e94795aSAndroid Build Coastguard Worker
54*9e94795aSAndroid Build Coastguard Worker  Returns:
55*9e94795aSAndroid Build Coastguard Worker    True if the GRF properties are valid.
56*9e94795aSAndroid Build Coastguard Worker  """
57*9e94795aSAndroid Build Coastguard Worker  grf_api_level = prop_list.get_value("ro.board.first_api_level")
58*9e94795aSAndroid Build Coastguard Worker  board_api_level = prop_list.get_value("ro.board.api_level")
59*9e94795aSAndroid Build Coastguard Worker
60*9e94795aSAndroid Build Coastguard Worker  if grf_api_level and board_api_level:
61*9e94795aSAndroid Build Coastguard Worker    grf_api_level = int(grf_api_level)
62*9e94795aSAndroid Build Coastguard Worker    board_api_level = int(board_api_level)
63*9e94795aSAndroid Build Coastguard Worker    if board_api_level < grf_api_level:
64*9e94795aSAndroid Build Coastguard Worker      sys.stderr.write("error: ro.board.api_level(%d) must not be less than "
65*9e94795aSAndroid Build Coastguard Worker                       "ro.board.first_api_level(%d)\n"
66*9e94795aSAndroid Build Coastguard Worker                       % (board_api_level, grf_api_level))
67*9e94795aSAndroid Build Coastguard Worker      return False
68*9e94795aSAndroid Build Coastguard Worker
69*9e94795aSAndroid Build Coastguard Worker  return True
70*9e94795aSAndroid Build Coastguard Worker
71*9e94795aSAndroid Build Coastguard Workerdef validate(prop_list):
72*9e94795aSAndroid Build Coastguard Worker  """Validate the properties.
73*9e94795aSAndroid Build Coastguard Worker
74*9e94795aSAndroid Build Coastguard Worker  If the value of a sysprop exceeds the max limit (91), it's an error, unless
75*9e94795aSAndroid Build Coastguard Worker  the sysprop is a read-only one.
76*9e94795aSAndroid Build Coastguard Worker
77*9e94795aSAndroid Build Coastguard Worker  Checks if there is no optional prop assignments.
78*9e94795aSAndroid Build Coastguard Worker
79*9e94795aSAndroid Build Coastguard Worker  Returns:
80*9e94795aSAndroid Build Coastguard Worker    True if nothing is wrong.
81*9e94795aSAndroid Build Coastguard Worker  """
82*9e94795aSAndroid Build Coastguard Worker  check_pass = True
83*9e94795aSAndroid Build Coastguard Worker  for p in prop_list.get_all_props():
84*9e94795aSAndroid Build Coastguard Worker    if len(p.value) > PROP_VALUE_MAX and not p.name.startswith("ro."):
85*9e94795aSAndroid Build Coastguard Worker      check_pass = False
86*9e94795aSAndroid Build Coastguard Worker      sys.stderr.write("error: %s cannot exceed %d bytes: " %
87*9e94795aSAndroid Build Coastguard Worker                       (p.name, PROP_VALUE_MAX))
88*9e94795aSAndroid Build Coastguard Worker      sys.stderr.write("%s (%d)\n" % (p.value, len(p.value)))
89*9e94795aSAndroid Build Coastguard Worker
90*9e94795aSAndroid Build Coastguard Worker    if p.is_optional():
91*9e94795aSAndroid Build Coastguard Worker      check_pass = False
92*9e94795aSAndroid Build Coastguard Worker      sys.stderr.write("error: found unresolved optional prop assignment:\n")
93*9e94795aSAndroid Build Coastguard Worker      sys.stderr.write(str(p) + "\n")
94*9e94795aSAndroid Build Coastguard Worker
95*9e94795aSAndroid Build Coastguard Worker  return check_pass
96*9e94795aSAndroid Build Coastguard Worker
97*9e94795aSAndroid Build Coastguard Workerdef override_optional_props(prop_list, allow_dup=False):
98*9e94795aSAndroid Build Coastguard Worker  """Override a?=b with a=c, if the latter exists
99*9e94795aSAndroid Build Coastguard Worker
100*9e94795aSAndroid Build Coastguard Worker  Overriding is done by deleting a?=b
101*9e94795aSAndroid Build Coastguard Worker  When there are a?=b and a?=c, then only the last one survives
102*9e94795aSAndroid Build Coastguard Worker  When there are a=b and a=c, then it's an error.
103*9e94795aSAndroid Build Coastguard Worker
104*9e94795aSAndroid Build Coastguard Worker  Returns:
105*9e94795aSAndroid Build Coastguard Worker    True if the override was successful
106*9e94795aSAndroid Build Coastguard Worker  """
107*9e94795aSAndroid Build Coastguard Worker  success = True
108*9e94795aSAndroid Build Coastguard Worker  for name in prop_list.get_all_names():
109*9e94795aSAndroid Build Coastguard Worker    props = prop_list.get_props(name)
110*9e94795aSAndroid Build Coastguard Worker    optional_props = [p for p in props if p.is_optional()]
111*9e94795aSAndroid Build Coastguard Worker    overriding_props = [p for p in props if not p.is_optional()]
112*9e94795aSAndroid Build Coastguard Worker    if len(overriding_props) > 1:
113*9e94795aSAndroid Build Coastguard Worker      # duplicated props are allowed when the all have the same value
114*9e94795aSAndroid Build Coastguard Worker      if all(overriding_props[0].value == p.value for p in overriding_props):
115*9e94795aSAndroid Build Coastguard Worker        for p in optional_props:
116*9e94795aSAndroid Build Coastguard Worker          p.delete("overridden by %s" % str(overriding_props[0]))
117*9e94795aSAndroid Build Coastguard Worker        continue
118*9e94795aSAndroid Build Coastguard Worker      # or if dup is explicitly allowed for compat reason
119*9e94795aSAndroid Build Coastguard Worker      if allow_dup:
120*9e94795aSAndroid Build Coastguard Worker        # this could left one or more optional props unresolved.
121*9e94795aSAndroid Build Coastguard Worker        # Convert them into non-optional because init doesn't understand ?=
122*9e94795aSAndroid Build Coastguard Worker        # syntax
123*9e94795aSAndroid Build Coastguard Worker        for p in optional_props:
124*9e94795aSAndroid Build Coastguard Worker          p.optional = False
125*9e94795aSAndroid Build Coastguard Worker        continue
126*9e94795aSAndroid Build Coastguard Worker
127*9e94795aSAndroid Build Coastguard Worker      success = False
128*9e94795aSAndroid Build Coastguard Worker      sys.stderr.write("error: found duplicate sysprop assignments:\n")
129*9e94795aSAndroid Build Coastguard Worker      for p in overriding_props:
130*9e94795aSAndroid Build Coastguard Worker        sys.stderr.write("%s\n" % str(p))
131*9e94795aSAndroid Build Coastguard Worker    elif len(overriding_props) == 1:
132*9e94795aSAndroid Build Coastguard Worker      for p in optional_props:
133*9e94795aSAndroid Build Coastguard Worker        p.delete("overridden by %s" % str(overriding_props[0]))
134*9e94795aSAndroid Build Coastguard Worker    else:
135*9e94795aSAndroid Build Coastguard Worker      if len(optional_props) > 1:
136*9e94795aSAndroid Build Coastguard Worker        for p in optional_props[:-1]:
137*9e94795aSAndroid Build Coastguard Worker          p.delete("overridden by %s" % str(optional_props[-1]))
138*9e94795aSAndroid Build Coastguard Worker      # Make the last optional one as non-optional
139*9e94795aSAndroid Build Coastguard Worker      optional_props[-1].optional = False
140*9e94795aSAndroid Build Coastguard Worker
141*9e94795aSAndroid Build Coastguard Worker  return success
142*9e94795aSAndroid Build Coastguard Worker
143*9e94795aSAndroid Build Coastguard Workerclass Prop:
144*9e94795aSAndroid Build Coastguard Worker
145*9e94795aSAndroid Build Coastguard Worker  def __init__(self, name, value, optional=False, comment=None):
146*9e94795aSAndroid Build Coastguard Worker    self.name = name.strip()
147*9e94795aSAndroid Build Coastguard Worker    self.value = value.strip()
148*9e94795aSAndroid Build Coastguard Worker    if comment != None:
149*9e94795aSAndroid Build Coastguard Worker      self.comments = [comment]
150*9e94795aSAndroid Build Coastguard Worker    else:
151*9e94795aSAndroid Build Coastguard Worker      self.comments = []
152*9e94795aSAndroid Build Coastguard Worker    self.optional = optional
153*9e94795aSAndroid Build Coastguard Worker
154*9e94795aSAndroid Build Coastguard Worker  @staticmethod
155*9e94795aSAndroid Build Coastguard Worker  def from_line(line):
156*9e94795aSAndroid Build Coastguard Worker    line = line.rstrip('\n')
157*9e94795aSAndroid Build Coastguard Worker    if line.startswith("#"):
158*9e94795aSAndroid Build Coastguard Worker      return Prop("", "", comment=line)
159*9e94795aSAndroid Build Coastguard Worker    elif "?=" in line:
160*9e94795aSAndroid Build Coastguard Worker      name, value = line.split("?=", 1)
161*9e94795aSAndroid Build Coastguard Worker      return Prop(name, value, optional=True)
162*9e94795aSAndroid Build Coastguard Worker    elif "=" in line:
163*9e94795aSAndroid Build Coastguard Worker      name, value = line.split("=", 1)
164*9e94795aSAndroid Build Coastguard Worker      return Prop(name, value, optional=False)
165*9e94795aSAndroid Build Coastguard Worker    else:
166*9e94795aSAndroid Build Coastguard Worker      # don't fail on invalid line
167*9e94795aSAndroid Build Coastguard Worker      # TODO(jiyong) make this a hard error
168*9e94795aSAndroid Build Coastguard Worker      return Prop("", "", comment=line)
169*9e94795aSAndroid Build Coastguard Worker
170*9e94795aSAndroid Build Coastguard Worker  def is_comment(self):
171*9e94795aSAndroid Build Coastguard Worker    return bool(self.comments and not self.name)
172*9e94795aSAndroid Build Coastguard Worker
173*9e94795aSAndroid Build Coastguard Worker  def is_optional(self):
174*9e94795aSAndroid Build Coastguard Worker    return (not self.is_comment()) and self.optional
175*9e94795aSAndroid Build Coastguard Worker
176*9e94795aSAndroid Build Coastguard Worker  def make_as_comment(self):
177*9e94795aSAndroid Build Coastguard Worker    # Prepend "#" to the last line which is the prop assignment
178*9e94795aSAndroid Build Coastguard Worker    if not self.is_comment():
179*9e94795aSAndroid Build Coastguard Worker      assignment = str(self).rsplit("\n", 1)[-1]
180*9e94795aSAndroid Build Coastguard Worker      self.comments.append("#" + assignment)
181*9e94795aSAndroid Build Coastguard Worker      self.name = ""
182*9e94795aSAndroid Build Coastguard Worker      self.value = ""
183*9e94795aSAndroid Build Coastguard Worker
184*9e94795aSAndroid Build Coastguard Worker  def delete(self, reason):
185*9e94795aSAndroid Build Coastguard Worker    self.comments.append("# Removed by post_process_props.py because " + reason)
186*9e94795aSAndroid Build Coastguard Worker    self.make_as_comment()
187*9e94795aSAndroid Build Coastguard Worker
188*9e94795aSAndroid Build Coastguard Worker  def __str__(self):
189*9e94795aSAndroid Build Coastguard Worker    assignment = []
190*9e94795aSAndroid Build Coastguard Worker    if not self.is_comment():
191*9e94795aSAndroid Build Coastguard Worker      operator = "?=" if self.is_optional() else "="
192*9e94795aSAndroid Build Coastguard Worker      assignment.append(self.name + operator + self.value)
193*9e94795aSAndroid Build Coastguard Worker    return "\n".join(self.comments + assignment)
194*9e94795aSAndroid Build Coastguard Worker
195*9e94795aSAndroid Build Coastguard Workerclass PropList:
196*9e94795aSAndroid Build Coastguard Worker
197*9e94795aSAndroid Build Coastguard Worker  def __init__(self, filename):
198*9e94795aSAndroid Build Coastguard Worker    with open(filename) as f:
199*9e94795aSAndroid Build Coastguard Worker      self.props = [Prop.from_line(l)
200*9e94795aSAndroid Build Coastguard Worker                    for l in f.readlines() if l.strip() != ""]
201*9e94795aSAndroid Build Coastguard Worker
202*9e94795aSAndroid Build Coastguard Worker  def get_all_props(self):
203*9e94795aSAndroid Build Coastguard Worker    return [p for p in self.props if not p.is_comment()]
204*9e94795aSAndroid Build Coastguard Worker
205*9e94795aSAndroid Build Coastguard Worker  def get_all_names(self):
206*9e94795aSAndroid Build Coastguard Worker    return set([p.name for p in self.get_all_props()])
207*9e94795aSAndroid Build Coastguard Worker
208*9e94795aSAndroid Build Coastguard Worker  def get_props(self, name):
209*9e94795aSAndroid Build Coastguard Worker    return [p for p in self.get_all_props() if p.name == name]
210*9e94795aSAndroid Build Coastguard Worker
211*9e94795aSAndroid Build Coastguard Worker  def get_value(self, name):
212*9e94795aSAndroid Build Coastguard Worker    # Caution: only the value of the first sysprop having the name is returned.
213*9e94795aSAndroid Build Coastguard Worker    return next((p.value for p in self.props if p.name == name), "")
214*9e94795aSAndroid Build Coastguard Worker
215*9e94795aSAndroid Build Coastguard Worker  def put(self, name, value):
216*9e94795aSAndroid Build Coastguard Worker    # Note: when there is an optional prop for the name, its value isn't changed.
217*9e94795aSAndroid Build Coastguard Worker    # Instead a new non-optional prop is appended, which will override the
218*9e94795aSAndroid Build Coastguard Worker    # optional prop. Otherwise, the new value might be overridden by an existing
219*9e94795aSAndroid Build Coastguard Worker    # non-optional prop of the same name.
220*9e94795aSAndroid Build Coastguard Worker    index = next((i for i,p in enumerate(self.props)
221*9e94795aSAndroid Build Coastguard Worker                  if p.name == name and not p.is_optional()), -1)
222*9e94795aSAndroid Build Coastguard Worker    if index == -1:
223*9e94795aSAndroid Build Coastguard Worker      self.props.append(Prop(name, value,
224*9e94795aSAndroid Build Coastguard Worker                             comment="# Auto-added by post_process_props.py"))
225*9e94795aSAndroid Build Coastguard Worker    else:
226*9e94795aSAndroid Build Coastguard Worker      self.props[index].comments.append(
227*9e94795aSAndroid Build Coastguard Worker          "# Value overridden by post_process_props.py. Original value: %s" %
228*9e94795aSAndroid Build Coastguard Worker          self.props[index].value)
229*9e94795aSAndroid Build Coastguard Worker      self.props[index].value = value
230*9e94795aSAndroid Build Coastguard Worker
231*9e94795aSAndroid Build Coastguard Worker  def write(self, filename):
232*9e94795aSAndroid Build Coastguard Worker    with open(filename, 'w+') as f:
233*9e94795aSAndroid Build Coastguard Worker      for p in self.props:
234*9e94795aSAndroid Build Coastguard Worker        f.write(str(p) + "\n")
235*9e94795aSAndroid Build Coastguard Worker
236*9e94795aSAndroid Build Coastguard Workerdef main(argv):
237*9e94795aSAndroid Build Coastguard Worker  parser = argparse.ArgumentParser(description="Post-process build.prop file")
238*9e94795aSAndroid Build Coastguard Worker  parser.add_argument("--allow-dup", dest="allow_dup", action="store_true",
239*9e94795aSAndroid Build Coastguard Worker                      default=False)
240*9e94795aSAndroid Build Coastguard Worker  parser.add_argument("filename")
241*9e94795aSAndroid Build Coastguard Worker  parser.add_argument("disallowed_keys", metavar="KEY", type=str, nargs="*")
242*9e94795aSAndroid Build Coastguard Worker  parser.add_argument("--sdk-version", type=int, required=True)
243*9e94795aSAndroid Build Coastguard Worker  parser.add_argument("--kernel-version-file-for-uffd-gc", required=True)
244*9e94795aSAndroid Build Coastguard Worker  args = parser.parse_args()
245*9e94795aSAndroid Build Coastguard Worker
246*9e94795aSAndroid Build Coastguard Worker  if not args.filename.endswith("/build.prop"):
247*9e94795aSAndroid Build Coastguard Worker    sys.stderr.write("bad command line: " + str(argv) + "\n")
248*9e94795aSAndroid Build Coastguard Worker    sys.exit(1)
249*9e94795aSAndroid Build Coastguard Worker
250*9e94795aSAndroid Build Coastguard Worker  props = PropList(args.filename)
251*9e94795aSAndroid Build Coastguard Worker  mangle_build_prop(props, args.kernel_version_file_for_uffd_gc)
252*9e94795aSAndroid Build Coastguard Worker  if not override_optional_props(props, args.allow_dup):
253*9e94795aSAndroid Build Coastguard Worker    sys.exit(1)
254*9e94795aSAndroid Build Coastguard Worker  if not validate_grf_props(props):
255*9e94795aSAndroid Build Coastguard Worker    sys.exit(1)
256*9e94795aSAndroid Build Coastguard Worker  if not validate(props):
257*9e94795aSAndroid Build Coastguard Worker    sys.exit(1)
258*9e94795aSAndroid Build Coastguard Worker
259*9e94795aSAndroid Build Coastguard Worker  # Drop any disallowed keys
260*9e94795aSAndroid Build Coastguard Worker  for key in args.disallowed_keys:
261*9e94795aSAndroid Build Coastguard Worker    for p in props.get_props(key):
262*9e94795aSAndroid Build Coastguard Worker      p.delete("%s is a disallowed key" % key)
263*9e94795aSAndroid Build Coastguard Worker
264*9e94795aSAndroid Build Coastguard Worker  props.write(args.filename)
265*9e94795aSAndroid Build Coastguard Worker
266*9e94795aSAndroid Build Coastguard Workerif __name__ == "__main__":
267*9e94795aSAndroid Build Coastguard Worker  main(sys.argv)
268