chromium/tools/grit/grit/format/resource_map.py

# Copyright 2012 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

'''This file contains item formatters for resource_map_header and
resource_map_source files.  A resource map is a mapping between resource names
(string) and the internal resource ID.'''


import os
from functools import partial

from grit import util


def GetFormatter(type):
  if type == 'resource_map_header':
    return partial(_FormatHeader, _GetItemName)
  if type == 'resource_file_map_source':
    return partial(_FormatSource, _GetItemPath)
  if type == 'resource_map_source':
    return partial(_FormatSource, _GetItemName)


def GetMapName(root):
  '''Get the name of the resource map based on the header file name.  E.g.,
  if our header filename is theme_resources.h, we name our resource map
  kThemeResourcesMap.

  |root| is the grd file root.'''
  outputs = root.GetOutputFiles()
  rc_header_file = None
  for output in outputs:
    if 'rc_header' == output.GetType():
      rc_header_file = output.GetFilename()
  if not rc_header_file:
    raise Exception('unable to find resource header filename')
  filename = os.path.splitext(os.path.split(rc_header_file)[1])[0]
  filename = filename[0].upper() + filename[1:]
  while True:
    pos = filename.find('_')
    if pos == -1 or pos >= len(filename):
      break
    filename = filename[:pos] + filename[pos + 1].upper() + filename[pos + 2:]
  return 'k' + filename


def _FormatHeader(get_key, root, lang='en', output_dir='.'):
  '''Create the header file for the resource mapping.  This file just declares
  an array of name/value pairs.'''
  unique_resource_count = sum(
      1 for item in _UniqueResourcesGenerator(get_key, root))
  return '''\
// This file is automatically generated by GRIT.  Do not edit.

#include <stddef.h>

#include "ui/base/webui/resource_path.h"

extern const webui::ResourcePath %(map_name)s[%(map_size)d];
extern const size_t %(map_name)sSize;
''' % {
      'map_name': GetMapName(root),
      'map_size': unique_resource_count,
  }


def _UniqueResourcesGenerator(get_key, root):
  id_map = root.GetIdMap()
  seen = set()
  for item in root.ActiveDescendants():
    if not item.IsResourceMapSource():
      continue
    tid = item.attrs['name']
    assert tid in id_map, 'unrecognized resource textual ID' \
      ' encountered: %s' % tid
    key = get_key(item)
    assert key not in seen, 'duplicate resource key encountered while' \
      ' generating resource map: %s' % key
    seen.add(key)
    yield (key, tid)


def _FormatSourceHeader(get_key, root, output_dir):
  '''Create the header of the C++ source file for the resource mapping.'''
  rc_header_file = None
  map_header_file = None
  for output in root.GetOutputFiles():
    type = output.GetType()
    if 'rc_header' == type:
      rc_header_file = util.MakeRelativePath(output_dir,
                                             output.GetOutputFilename())
    elif 'resource_map_header' == type:
      map_header_file = util.MakeRelativePath(output_dir,
                                              output.GetOutputFilename())
  if not rc_header_file or not map_header_file:
    raise Exception('resource_map_source output type requires '
        'a resource_map_header and rc_header outputs')
  unique_resource_count = sum(
      1 for item in _UniqueResourcesGenerator(get_key, root))
  return '''\
// This file is automatically generated by GRIT.  Do not edit.

#include "%(map_header_file)s"

#include <stddef.h>

#include <iterator>

#include "%(rc_header_file)s"

const webui::ResourcePath %(map_name)s[%(map_size)d] = {
''' % {
      'map_header_file': map_header_file,
      'rc_header_file': rc_header_file,
      'map_name': GetMapName(root),
      'map_size': unique_resource_count,
  }


def _FormatSourceFooter(root):
  # Return the footer text.
  return '''\
};

const size_t %(map_name)sSize = std::size(%(map_name)s);
''' % {
      'map_name': GetMapName(root)
  }

def _FormatSource(get_key, root, lang, output_dir):
  yield _FormatSourceHeader(get_key, root, output_dir)
  for (key, tid) in _UniqueResourcesGenerator(get_key, root):
    yield '  {"%s", %s},\n' % (key, tid)
  yield _FormatSourceFooter(root)


def _GetItemName(item):
  return item.attrs['name']

def _GetItemPath(item):
  resource_path = item.attrs.get('resource_path', '')
  if resource_path:
    return resource_path

  path = item.GetInputPath().replace("\\", "/")

  # For the case of generated files such as
  # out/gchrome/${root_gen_dir}/ui/webui/resources/js/foo.js
  # |resource_path| must be provided. It will be used as the |path| in the
  # generated ResourcePath entry. For WebUI files, it will also be used as
  # the URL subpath under which a file will be served at runtime.
  assert item.attrs.get('use_base_dir', 'true') == 'true', \
      'resource_path attribute missing for %s. Generated files must specify' \
      ' resource_path' % item.attrs.get('name')

  assert '$' not in path, 'all variables should have been expanded'
  return path