# Copyright (C) 2022 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. # # 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.
"""
Finalize resource values in <staging-public-group> tags and convert those to <staging-public-group-final>
Build and execute a python binary using soong. This will included the aconfig flags for the lunched configuration.
"""
Created finalized <public> declarations for staging resources, ignoring them if they've been
prefixed with removed_. The IDs are assigned without holes starting from the last ID for that
type currently finalized in public-final.xml. """ def finalize_item(comment_and_item):
print("Processing:\n" + comment_and_item)
name = re.search('<public name="(.+?)"',comment_and_item, flags=re.DOTALL).group(1) if re.match('removed_.+', name): # Remove it from <staging-public-group> in public-staging.xml # Include it as is in <staging-public-group-final> in public-final.xml # Don't assign an id in public-final.xml return ("", comment_and_item, "")
match = re.search(r'<!-- @FlaggedApi\((.+?)\)', comment, flags=re.DOTALL) if match:
flag = match.group(1) else:
flag = NO_FLAG_MAGIC_CONSTANT
if flag.startswith("\""): # Flag is a string value, just remove "
flag = flag.replace("\"", "") else: # Flag is a java constant, convert to string value
flag = flag.replace(".Flags.FLAG_", ".").lower()
if flag notin _aconfig_map: raise Exception("Unknown flag: " + flag)
# Any flag that is not both READ_ONLY and ENABLED is left in the staging file. ifnot _aconfig_map[flag]:
_non_finalized_flags[flag].append(name) # Keep it as is in <staging-public-group> in public-staging.xml # Include as magic constant "removed_" in <staging-public-group-final> in public-final.xml # Don't assign an id in public-final.xml return (comment_and_item, " <public name=\"removed_\" />\n", "")
_finalized_flags[flag].append(name)
id = _type_ids[_type]
_type_ids[_type] += 1
# Removes one indentation step to align the comment with the item outside the
comment = re.sub("^ ", "", comment, flags=re.MULTILINE)
# Remove from <staging-public-group> in public-staging.xml # Include as is in <staging-public-group-final> in public-final.xml # Assign an id in public-final.xml return ("", comment_and_item, comment + ' <public type="%s" name="%s" id="%s" />\n' % (_type, name, '0x{0:0{1}x}'.format(id, 8)))
"""
Finalizes staging-public-groups if they have any entries in them. Also keeps track of the
lowest first-id of the non-empty groups so that the next release's staging-public-groups can
be assigned the next down shifted first-id. """ def finalize_group(raw): global _type, _lowest_staging_first_id
_type = raw.group(1)
id = int(raw.group(2), 16)
_type_ids[_type] = _type_ids.get(_type, id)
all = re.findall(' *<!--.*?<public name=".+?" */>\n', raw.group(3), flags=re.DOTALL)
res = ""
group_matches = "" for match in all:
(staging_group, final_group, final_id_assignment) = finalize_item(match)
if staging_group:
_not_finalized[_type].append(staging_group)
if final_group:
group_matches += final_group
if final_id_assignment:
res += final_id_assignment
# Only add it to final.xml if new ids were actually assigned if res:
res = '<staging-public-group-final type="%s" first-id="%s">\n%s </staging-public-group-final>\n\n%s' % (_type, raw.group(2), group_matches, res)
# Potenitally bump _lowest_staging_first_id if this group had any flags (finalize or not). if all:
_lowest_staging_first_id = min(id, _lowest_staging_first_id)
return res
"""
Collects the max ID for each resType so that the new IDs can be assigned afterwards """ def collect_ids(raw): for m in re.finditer(r'<public type="(.+?)" name=".+?" id="(.+?)" />', raw):
type = m.group(1)
id = int(m.group(2), 16)
_type_ids[type] = max(id + 1, _type_ids.get(type, 0))
try: with importlib.resources.files('res').joinpath('all_aconfig_declarations.pb').open('rb') as f:
parsed_flags = aconfig_pb2.parsed_flags.FromString(f.read()) except FileNotFoundError as error:
print(error)
print("Could not access aconfig flags. Did you build the script using soong?")
sys.exit()
for flag in parsed_flags.parsed_flag:
key = "{}.{}".format(flag.package, flag.name)
value = (
flag.permission == aconfig_pb2.flag_permission.READ_ONLY and flag.state == aconfig_pb2.flag_state.ENABLED
)
_aconfig_map[key] = value
_aconfig_map[NO_FLAG_MAGIC_CONSTANT] = False
with open(sys.argv[1], "r+") as stagingFile: with open(sys.argv[2], "r+") as finalFile:
existing = finalFile.read() # Cut out the closing resources tag so that it can be concatenated easily later
existing = "\n".join(existing.rsplit("</resources>", 1))
# Collect the IDs from the existing already finalized resources
collect_ids(existing)
# First write the existing finalized declarations and then append the new stuff
finalFile.seek(0)
finalFile.write(existing.strip("\n"))
finalFile.write("\n\n")
finalFile.write(staging.strip("\n"))
finalFile.write("\n")
finalFile.truncate()
stagingFile.seek(0) # Include the documentation from public-staging.xml that was previously split out
stagingFile.write(stagingSplit[0]) # Write the next platform header
stagingFile.write("<resources>\n\n")
stagingFile.write(" <!-- ===============================================================\n")
stagingFile.write(" Resources added in version NEXT of the platform\n\n")
stagingFile.write(" NOTE: After this version of the platform is forked, changes cannot be made to the root\n")
stagingFile.write(" branch's groups for that release. Only merge changes to the forked platform branch.\n")
stagingFile.write(" =============================================================== -->\n")
stagingFile.write(" <eat-comment/>\n\n")
# Seed the next release's staging-public-groups as empty declarations, # so its easy for another developer to expose a new public resource
nextId = _lowest_staging_first_id - 0x00010000 for resType in resTypes:
stagingFile.write(' <staging-public-group type="%s" first-id="%s">\n'
% (resType, '0x{0:0{1}x}'.format(nextId, 8))) for item in _not_finalized[resType]:
stagingFile.write(item)
stagingFile.write(' </staging-public-group>\n\n')
nextId -= 0x00010000
# Close the resources tag and truncate, since the file will be shorter than the previous
stagingFile.write("</resources>\n")
stagingFile.truncate()
_non_finalized_flags = sorted([f" {flag}: {resource}"for flag, values in _non_finalized_flags.items() for resource in values])
print(f"\n{len(_non_finalized_flags)} resource(s) were NOT finalized:")
print("\n".join(_non_finalized_flags))
_finalized_flags = sorted([f" {flag}: {resource}"for flag, values in _finalized_flags.items() for resource in values])
print(f"\n{len(_finalized_flags)} resource(s) were finalized:")
print("\n".join(_finalized_flags))
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.