195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python
295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved.
301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be
401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file.
501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''The 'grit build' tool along with integration for this tool with the
701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgSCons build system.
801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''
901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport filecmp
1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport getopt
1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport os
1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport shutil
1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport sys
1501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import grd_reader
1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import util
1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.tool import interface
1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import shortcuts
2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
22ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org# It would be cleaner to have each module register itself, but that would
23ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org# require importing all of them on every run of GRIT.
24ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org'''Map from <output> node types to modules under grit.format.'''
25ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org_format_modules = {
26ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'android':                  'android_xml',
27ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'c_format':                 'c_format',
28ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'chrome_messages_json':     'chrome_messages_json',
29ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'data_package':             'data_pack',
30ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'js_map_format':            'js_map_format',
31ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'rc_all':                   'rc',
32ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'rc_translateable':         'rc',
33ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'rc_nontranslateable':      'rc',
34ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'rc_header':                'rc_header',
35ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'resource_map_header':      'resource_map',
36ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'resource_map_source':      'resource_map',
37ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  'resource_file_map_source': 'resource_map',
38ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org}
39ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org_format_modules.update((type, 'policy_templates.template_formatter')
40ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for type in 'adm plist plist_strings admx adml doc json reg'.split())
41ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
42ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
43ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.orgdef GetFormatter(type):
44ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  modulename = 'grit.format.' + _format_modules[type]
45ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  __import__(modulename)
46ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  module = sys.modules[modulename]
47ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  try:
48ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    return module.Format
49ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  except AttributeError:
50ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    return module.GetFormatter(type)
51ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
52ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org
5301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass RcBuilder(interface.Tool):
5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''A tool that builds RC files and resource header files for compilation.
5501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgUsage:  grit build [-o OUTPUTDIR] [-D NAME[=VAL]]*
5701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgAll output options for this tool are specified in the input file (see
5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'grit help' for details on how to specify the input file - it is a global
6001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgoption).
6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgOptions:
6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  -o OUTPUTDIR      Specify what directory output paths are relative to.
6501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    Defaults to the current directory.
6601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  -D NAME[=VAL]     Specify a C-preprocessor-like define NAME with optional
6801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    value VAL (defaults to 1) which will be used to control
6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    conditional inclusion of resources.
7001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  -E NAME=VALUE     Set environment variable NAME to VALUE (within grit).
7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
730dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org  -f FIRSTIDSFILE   Path to a python file that specifies the first id of
740dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                    value to use for resources.  A non-empty value here will
750dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                    override the value specified in the <grit> node's
760dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                    first_ids_file.
7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  -w WHITELISTFILE  Path to a file containing the string names of the
7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                    resources to include.  Anything not listed is dropped.
8001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
81abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org  -t PLATFORM       Specifies the platform the build is targeting; defaults
82abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org                    to the value of sys.platform. The value provided via this
83abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org                    flag should match what sys.platform would report for your
84abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org                    target platform; see grit.node.base.EvaluateCondition.
8501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
8601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgConditional inclusion of resources only affects the output of files which
8701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgcontrol which resources get linked into a binary, e.g. it affects .rc files
8801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgmeant for compilation but it does not affect resource header files (that define
8901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgIDs).  This helps ensure that values of IDs stay the same, that all messages
9001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgare exported to translation interchange files (e.g. XMB files), etc.
9101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''
9201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def ShortDescription(self):
9401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return 'A tool that builds RC files for compilation.'
9501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
9601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Run(self, opts, args):
9701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.output_directory = '.'
980dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    first_ids_file = None
9901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    whitelist_filenames = []
100abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org    target_platform = None
101247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    dep_dir = None
102247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    (own_opts, args) = getopt.getopt(args, 'o:D:E:f:w:t:', ('dep-dir=',))
10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for (key, val) in own_opts:
10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if key == '-o':
10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.output_directory = val
10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      elif key == '-D':
10777cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org        name, val = util.ParseDefine(val)
10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.defines[name] = val
10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      elif key == '-E':
110ef7918f5c87325d97a94438b3adf18e1bc799b7cjoi@chromium.org        (env_name, env_value) = val.split('=', 1)
11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        os.environ[env_name] = env_value
11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      elif key == '-f':
1130dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org        # TODO(joi@chromium.org): Remove this override once change
1140dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org        # lands in WebKit.grd to specify the first_ids_file in the
1150dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org        # .grd itself.
1160dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org        first_ids_file = val
11701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      elif key == '-w':
11801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        whitelist_filenames.append(val)
119abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org      elif key == '-t':
120abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org        target_platform = val
121247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      elif key == '--dep-dir':
122247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org        dep_dir = val
12301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
12401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if len(args):
12577cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org      print 'This tool takes no tool-specific arguments.'
12601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return 2
12701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.SetOptions(opts)
12801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.scons_targets:
12901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.VerboseOut('Using SCons targets to identify files to output.\n')
13001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
13101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.VerboseOut('Output directory: %s (absolute path: %s)\n' %
13201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                      (self.output_directory,
13301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                       os.path.abspath(self.output_directory)))
13401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
13501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if whitelist_filenames:
13601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.whitelist_names = set()
13701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      for whitelist_filename in whitelist_filenames:
13801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.VerboseOut('Using whitelist: %s\n' % whitelist_filename);
139b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        whitelist_contents = util.ReadFile(whitelist_filename, util.RAW_TEXT)
140b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        self.whitelist_names.update(whitelist_contents.strip().split('\n'))
14101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1420dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    self.res = grd_reader.Parse(opts.input,
1430dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                                debug=opts.extra_verbose,
1440dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org                                first_ids_file=first_ids_file,
145abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org                                defines=self.defines,
146abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org                                target_platform=target_platform)
147b012d530d2c6c921727e1136d0939e2649e4f77ejoi@chromium.org    # Set an output context so that conditionals can use defines during the
148b012d530d2c6c921727e1136d0939e2649e4f77ejoi@chromium.org    # gathering stage; we use a dummy language here since we are not outputting
149b012d530d2c6c921727e1136d0939e2649e4f77ejoi@chromium.org    # a specific language.
150783385ad40d453977d8b9d537e204f851e21591abenrg@chromium.org    self.res.SetOutputLanguage('en')
151ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    self.res.RunGatherers()
15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.Process()
153247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
154247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    if dep_dir:
155247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      self.GenerateDepfile(opts.input, dep_dir)
156247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
15701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    return 0
15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
159705a118ab4a1f2fe348fccdcd4786a0b5bf426ecjoi@chromium.org  def __init__(self, defines=None):
160b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    # Default file-creation function is built-in open().  Only done to allow
16101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # overriding by unit test.
162b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org    self.fo_create = open
16301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
16401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # key/value pairs of C-preprocessor like defines that are used for
16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # conditional output of resources
16677cbaa8b1f1af05d8ba2c2a951c74e7909318830joi@chromium.org    self.defines = defines or {}
16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # self.res is a fully-populated resource tree if Run()
16901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # has been called, otherwise None.
17001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.res = None
17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Set to a list of filenames for the output nodes that are relative
17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # to the current working directory.  They are in the same order as the
17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # output nodes in the file.
17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.scons_targets = None
17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # The set of names that are whitelisted to actually be included in the
17801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # output.
17901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.whitelist_names = None
18001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
18101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
182b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org  @staticmethod
18301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def AddWhitelistTags(start_node, whitelist_names):
18401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Walk the tree of nodes added attributes for the nodes that shouldn't
18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # be written into the target files (skip markers).
18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    from grit.node import include
18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    from grit.node import message
188ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    for node in start_node:
18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # Same trick data_pack.py uses to see what nodes actually result in
19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # real items.
19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if (isinstance(node, include.IncludeNode) or
19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          isinstance(node, message.MessageNode)):
19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        text_ids = node.GetTextualIds()
19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # Mark the item to be skipped if it wasn't in the whitelist.
195ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org        if text_ids and text_ids[0] not in whitelist_names:
19601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          node.SetWhitelistMarkedAsSkip(True)
19701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
198b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org  @staticmethod
19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def ProcessNode(node, output_node, outfile):
20001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''Processes a node in-order, calling its formatter before and after
20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    recursing to its children.
20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
20301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Args:
20401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      node: grit.node.base.Node subclass
205ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org      output_node: grit.node.io.OutputNode
20601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      outfile: open filehandle
20701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    '''
20801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    base_dir = util.dirname(output_node.GetOutputFilename())
20901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
210ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    formatter = GetFormatter(output_node.GetType())
211ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    formatted = formatter(node, output_node.GetLanguage(), output_dir=base_dir)
212ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    outfile.writelines(formatted)
21301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def Process(self):
21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Update filenames with those provided by SCons if we're being invoked
21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # from SCons.  The list of SCons targets also includes all <structure>
21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # node outputs, but it starts with our output files, in the order they
21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # occur in the .grd
22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.scons_targets:
22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      assert len(self.scons_targets) >= len(self.res.GetOutputFiles())
22201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      outfiles = self.res.GetOutputFiles()
22301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      for ix in range(len(outfiles)):
22401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        outfiles[ix].output_filename = os.path.abspath(
22501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          self.scons_targets[ix])
22601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
22701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      for output in self.res.GetOutputFiles():
22801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        output.output_filename = os.path.abspath(os.path.join(
22901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          self.output_directory, output.GetFilename()))
23001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
23101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # If there are whitelisted names, tag the tree once up front, this way
23201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # while looping through the actual output, it is just an attribute check.
23301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.whitelist_names:
23401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.AddWhitelistTags(self.res, self.whitelist_names)
23501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
23601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    for output in self.res.GetOutputFiles():
23701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.VerboseOut('Creating %s...' % output.GetFilename())
23801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
23901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # Microsoft's RC compiler can only deal with single-byte or double-byte
24001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # files (no UTF-8), so we make all RC files UTF-16 to support all
24101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # character sets.
24201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if output.GetType() in ('rc_header', 'resource_map_header',
24301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org          'resource_map_source', 'resource_file_map_source'):
24401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        encoding = 'cp1252'
24539c335c3faa47701974a3ba3f7f8175947efc248newt@chromium.org      elif output.GetType() in ('android', 'c_format', 'js_map_format', 'plist',
246140682b7c724552cbac145cbafaa6a88ce757123sergeyu@chromium.org                                'plist_strings', 'doc', 'json'):
24701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        encoding = 'utf_8'
248140682b7c724552cbac145cbafaa6a88ce757123sergeyu@chromium.org      elif output.GetType() in ('chrome_messages_json'):
249140682b7c724552cbac145cbafaa6a88ce757123sergeyu@chromium.org        # Chrome Web Store currently expects BOM for UTF-8 files :-(
250140682b7c724552cbac145cbafaa6a88ce757123sergeyu@chromium.org        encoding = 'utf-8-sig'
25101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
25201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # TODO(gfeher) modify here to set utf-8 encoding for admx/adml
25301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        encoding = 'utf_16'
25401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
255b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      # Set the context, for conditional inclusion of resources
256b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      self.res.SetOutputLanguage(output.GetLanguage())
257b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      self.res.SetOutputContext(output.GetContext())
258b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      self.res.SetDefines(self.defines)
259b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org
26001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # Make the output directory if it doesn't exist.
261247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      self.MakeDirectoriesTo(output.GetOutputFilename())
262b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org
26301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # Write the results to a temporary file and only overwrite the original
26401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # if the file changed.  This avoids unnecessary rebuilds.
26501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      outfile = self.fo_create(output.GetOutputFilename() + '.tmp', 'wb')
26601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
26701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if output.GetType() != 'data_package':
26801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        outfile = util.WrapOutputStream(outfile, encoding)
26901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # Iterate in-order through entire resource tree, calling formatters on
27101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # the entry into a node and on exit out of it.
272b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org      with outfile:
273b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.org        self.ProcessNode(self.res, output, outfile)
27401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
27501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # Now copy from the temp file back to the real output, but on Windows,
27601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # only if the real output doesn't exist or the contents of the file
27701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # changed.  This prevents identical headers from being written and .cc
27801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      # files from recompiling (which is painful on Windows).
27901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if not os.path.exists(output.GetOutputFilename()):
28001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        os.rename(output.GetOutputFilename() + '.tmp',
28101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                  output.GetOutputFilename())
28201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      else:
28301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # CHROMIUM SPECIFIC CHANGE.
28401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # This clashes with gyp + vstudio, which expect the output timestamp
28501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        # to change on a rebuild, even if nothing has changed.
28601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        #files_match = filecmp.cmp(output.GetOutputFilename(),
28701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        #    output.GetOutputFilename() + '.tmp')
28801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        #if (output.GetType() != 'rc_header' or not files_match
28901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        #    or sys.platform != 'win32'):
29001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        shutil.copy2(output.GetOutputFilename() + '.tmp',
29101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org                     output.GetOutputFilename())
29201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        os.remove(output.GetOutputFilename() + '.tmp')
29301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
29401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.VerboseOut(' done.\n')
29501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
29601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Print warnings if there are any duplicate shortcuts.
29701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    warnings = shortcuts.GenerateDuplicateShortcutsWarnings(
29801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.res.UberClique(), self.res.GetTcProject())
29901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if warnings:
30001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      print '\n'.join(warnings)
30101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
30201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Print out any fallback warnings, and missing translation errors, and
30301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # exit with an error code if there are missing translations in a non-pseudo
30401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # and non-official build.
30501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    warnings = (self.res.UberClique().MissingTranslationsReport().
30601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        encode('ascii', 'replace'))
30799515cd09d78391b5ec3145ee6b7939220612e8dtony@chromium.org    if warnings:
30899515cd09d78391b5ec3145ee6b7939220612e8dtony@chromium.org      self.VerboseOut(warnings)
30901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.res.UberClique().HasMissingTranslations():
3101eddb32f808ac5930f24b1954b5c949b273a3869joi@chromium.org      print self.res.UberClique().missing_translations_
31101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      sys.exit(-1)
312247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
313247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org  def GenerateDepfile(self, input_filename, dep_dir):
314247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    '''Generate a depfile that contains the imlicit dependencies of the input
315247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    grd. The depfile will be in the same format as a makefile, and will contain
316247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    references to files relative to |dep_dir|. It will be put in the same
317247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    directory as the generated outputs.
318247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
319247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    For example, supposing we have three files in a directory src/
320247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
321247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    src/
322247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      blah.grd    <- depends on input{1,2}.xtb
323247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      input1.xtb
324247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      input2.xtb
325247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
326247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    and we run
327247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
328247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      grit -i blah.grd -o ../out/gen --dep-dir ../out
329247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
330247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    from the directory src/ we will generate a depfile ../out/gen/blah.grd.d
331247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    that has the contents
332247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
333247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      gen/blah.grd.d: ../src/input1.xtb ../src/input2.xtb
334247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
335247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    Note that all paths in the depfile are relative to ../out, the dep-dir.
336247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    '''
337247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    depsfile_basename = os.path.basename(input_filename + '.d')
338247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    depfile = os.path.abspath(
339247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org        os.path.join(self.output_directory, depsfile_basename))
340247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    dep_dir = os.path.abspath(dep_dir)
341247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    # The path prefix to prepend to dependencies in the depfile.
342247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    prefix = os.path.relpath(os.getcwd(), dep_dir)
343247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    # The path that the depfile refers to itself by.
344247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    self_ref_depfile = os.path.relpath(depfile, dep_dir)
345247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    infiles = self.res.GetInputFiles()
346247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    deps_text = ' '.join([os.path.join(prefix, i) for i in infiles])
347247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    depfile_contents = self_ref_depfile + ': ' + deps_text
348247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    self.MakeDirectoriesTo(depfile)
349247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    outfile = self.fo_create(depfile, 'wb')
350247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    outfile.writelines(depfile_contents)
351247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org
352247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org  @staticmethod
353247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org  def MakeDirectoriesTo(file):
354247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    '''Creates directories necessary to contain |file|.'''
355247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    dir = os.path.split(file)[0]
356247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org    if not os.path.exists(dir):
357247c890730ffcd4932c99117272aba944f347cc6joi@chromium.org      os.makedirs(dir)
358