chromium/tools/grit/grit/format/gen_predetermined_ids.py

#!/usr/bin/env python3
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
A tool to generate a predetermined resource ids file that can be used as an
input to grit via the -p option. This is meant to be run manually every once in
a while and its output checked in. See tools/gritsettings/README.md for details.
"""


import os
import re
import sys

# Regular expression for parsing the #define macro format. Matches both the
# version of the macro with allowlist support and the one without. For example,
# Without generate allowlist flag:
#   #define IDS_FOO_MESSAGE 1234
# With generate allowlist flag:
#   #define IDS_FOO_MESSAGE (::ui::AllowlistedResource<1234>(), 1234)
RESOURCE_EXTRACT_REGEX = re.compile(r'^#define (\S*).* (\d+)\)?$', re.MULTILINE)

ORDERED_RESOURCE_IDS_REGEX = re.compile(r'^Resource=(\d*)$', re.MULTILINE)


def _GetResourceNameIdPairsIter(string_to_scan):
  """Gets an iterator of the resource name and id pairs of the given string.

  Scans the input string for lines of the form "#define NAME ID" and returns
  an iterator over all matching (NAME, ID) pairs.

  Args:
    string_to_scan: The input string to scan.

  Yields:
    A tuple of name and id.
  """
  for match in RESOURCE_EXTRACT_REGEX.finditer(string_to_scan):
    yield match.group(1, 2)


def _ReadOrderedResourceIds(path):
  """Reads ordered resource ids from the given file.

  The resources are expected to be of the format produced by running Chrome
  with --print-resource-ids command line.

  Args:
    path: File path to read resource ids from.

  Returns:
    An array of ordered resource ids.
  """
  ordered_resource_ids = []
  with open(path) as f:
    for match in ORDERED_RESOURCE_IDS_REGEX.finditer(f.read()):
      ordered_resource_ids.append(int(match.group(1)))
  return ordered_resource_ids


def GenerateResourceMapping(original_resources, ordered_resource_ids):
  """Generates a resource mapping from the ordered ids and the original mapping.

  The returned dict will assign new ids to ordered_resource_ids numerically
  increasing from 101.

  Args:
    original_resources: A dict of original resource ids to resource names.
    ordered_resource_ids: An array of ordered resource ids.

  Returns:
    A dict of resource ids to resource names.
  """
  output_resource_map = {}
  # 101 is used as the starting value since other parts of GRIT require it to be
  # the minimum (e.g. rc_header.py) based on Windows resource numbering.
  next_id = 101
  for original_id in ordered_resource_ids:
    resource_name = original_resources[original_id]
    output_resource_map[next_id] = resource_name
    next_id += 1
  return output_resource_map


def ReadResourceIdsFromFile(file, original_resources):
  """Reads resource ids from a GRIT-produced header file.

  Args:
    file: File to a GRIT-produced header file to read from.
    original_resources: Dict of resource ids to resource names to add to.
  """
  for resource_name, resource_id in _GetResourceNameIdPairsIter(file.read()):
    original_resources[int(resource_id)] = resource_name


def _ReadOriginalResourceIds(out_dir):
  """Reads resource ids from GRIT header files in the specified directory.

  Args:
    out_dir: A Chrome build output directory (e.g. out/gn) to scan.

  Returns:
    A dict of resource ids to resource names.
  """
  original_resources = {}
  for root, dirnames, filenames in os.walk(out_dir + '/gen'):
    for filename in filenames:
      if filename.endswith(
          ('_resources.h', '_settings.h', '_strings.h', '_synonyms.h')):
        with open(os.path.join(root, filename)) as f:
          ReadResourceIdsFromFile(f, original_resources)
  return original_resources


def _GeneratePredeterminedIdsFile(ordered_resources_file, out_dir):
  """Generates a predetermined ids file.

  Args:
    ordered_resources_file: File path to read ordered resource ids from.
    out_dir: A Chrome build output directory (e.g. out/gn) to scan.

  Returns:
    A dict of resource ids to resource names.
  """
  original_resources = _ReadOriginalResourceIds(out_dir)
  ordered_resource_ids = _ReadOrderedResourceIds(ordered_resources_file)
  output_resource_map = GenerateResourceMapping(original_resources,
                                                ordered_resource_ids)
  for res_id in sorted(output_resource_map.keys()):
    print(output_resource_map[res_id], res_id)


def main(argv):
  if len(argv) != 2:
    print("usage: gen_predetermined_ids.py <ordered_resources_file> <out_dir>")
    sys.exit(1)
  ordered_resources_file, out_dir = argv[0], argv[1]
  _GeneratePredeterminedIdsFile(ordered_resources_file, out_dir)


if '__main__' == __name__:
  main(sys.argv[1:])