1#!/usr/bin/env python
2# Copyright (c) 2012 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6'''Tool to determine inputs and outputs of a grit file.
7'''
8
9from __future__ import print_function
10
11import optparse
12import os
13import posixpath
14import sys
15
16from grit import grd_reader
17from grit import util
18
19class WrongNumberOfArguments(Exception):
20  pass
21
22
23def Outputs(filename, defines, ids_file, target_platform=None):
24  grd = grd_reader.Parse(
25      filename, defines=defines, tags_to_ignore=set(['messages']),
26      first_ids_file=ids_file, target_platform=target_platform)
27
28  target = []
29  lang_folders = {}
30  # Add all explicitly-specified output files
31  for output in grd.GetOutputFiles():
32    path = output.GetFilename()
33    target.append(path)
34
35    if path.endswith('.h'):
36      path, filename = os.path.split(path)
37    if output.attrs['lang']:
38      lang_folders[output.attrs['lang']] = os.path.dirname(path)
39
40  return [t.replace('\\', '/') for t in target]
41
42
43def GritSourceFiles():
44  files = []
45  grit_root_dir = os.path.relpath(os.path.dirname(__file__), os.getcwd())
46  for root, dirs, filenames in os.walk(grit_root_dir):
47    grit_src = [os.path.join(root, f) for f in filenames
48                if f.endswith('.py') and not f.endswith('_unittest.py')]
49    files.extend(grit_src)
50  return sorted(files)
51
52
53def Inputs(filename, defines, ids_file, target_platform=None):
54  grd = grd_reader.Parse(
55      filename, debug=False, defines=defines, tags_to_ignore=set(['message']),
56      first_ids_file=ids_file, target_platform=target_platform)
57  files = set()
58  for lang, ctx, fallback in grd.GetConfigurations():
59    # TODO(tdanderson): Refactor all places which perform the action of setting
60    #                   output attributes on the root. See crbug.com/503637.
61    grd.SetOutputLanguage(lang or grd.GetSourceLanguage())
62    grd.SetOutputContext(ctx)
63    grd.SetFallbackToDefaultLayout(fallback)
64    for node in grd.ActiveDescendants():
65      with node:
66        if (node.name == 'structure' or node.name == 'skeleton' or
67            (node.name == 'file' and node.parent and
68             node.parent.name == 'translations')):
69          path = node.GetInputPath()
70          if path is not None:
71            files.add(grd.ToRealPath(path))
72
73          # If it's a flattened node, grab inlined resources too.
74          if node.name == 'structure' and node.attrs['flattenhtml'] == 'true':
75            node.RunPreSubstitutionGatherer()
76            files.update(node.GetHtmlResourceFilenames())
77        elif node.name == 'grit':
78          first_ids_file = node.GetFirstIdsFile()
79          if first_ids_file:
80            files.add(first_ids_file)
81        elif node.name == 'include':
82          files.add(grd.ToRealPath(node.GetInputPath()))
83          # If it's a flattened node, grab inlined resources too.
84          if node.attrs['flattenhtml'] == 'true':
85            files.update(node.GetHtmlResourceFilenames())
86        elif node.name == 'part':
87          files.add(util.normpath(os.path.join(os.path.dirname(filename),
88                                               node.GetInputPath())))
89
90  cwd = os.getcwd()
91  return [os.path.relpath(f, cwd) for f in sorted(files)]
92
93
94def PrintUsage():
95  print('USAGE: ./grit_info.py --inputs [-D foo] [-f resource_ids] <grd-file>')
96  print('       ./grit_info.py --outputs [-D foo] [-f resource_ids] ' +
97        '<out-prefix> <grd-file>')
98
99
100def DoMain(argv):
101  os.environ['cwd'] = os.getcwd()
102
103  parser = optparse.OptionParser()
104  parser.add_option("--inputs", action="store_true", dest="inputs")
105  parser.add_option("--outputs", action="store_true", dest="outputs")
106  parser.add_option("-D", action="append", dest="defines", default=[])
107  # grit build also supports '-E KEY=VALUE', support that to share command
108  # line flags.
109  parser.add_option("-E", action="append", dest="build_env", default=[])
110  parser.add_option("-p", action="store", dest="predetermined_ids_file")
111  parser.add_option("-w", action="append", dest="whitelist_files", default=[])
112  parser.add_option("-f", dest="ids_file", default="")
113  parser.add_option("-t", dest="target_platform", default=None)
114
115  options, args = parser.parse_args(argv)
116
117  defines = {}
118  for define in options.defines:
119    name, val = util.ParseDefine(define)
120    defines[name] = val
121
122  for env_pair in options.build_env:
123    (env_name, env_value) = env_pair.split('=', 1)
124    os.environ[env_name] = env_value
125
126  if options.inputs:
127    if len(args) > 1:
128      raise WrongNumberOfArguments("Expected 0 or 1 arguments for --inputs.")
129
130    inputs = []
131    if len(args) == 1:
132      filename = args[0]
133      inputs = Inputs(filename, defines, options.ids_file,
134                      options.target_platform)
135
136    # Add in the grit source files.  If one of these change, we want to re-run
137    # grit.
138    inputs.extend(GritSourceFiles())
139    inputs = [f.replace('\\', '/') for f in inputs]
140
141    if len(args) == 1:
142      # Include grd file as second input (works around gyp expecting it).
143      inputs.insert(1, args[0])
144    if options.whitelist_files:
145      inputs.extend(options.whitelist_files)
146    return '\n'.join(inputs)
147  elif options.outputs:
148    if len(args) != 2:
149      raise WrongNumberOfArguments(
150          "Expected exactly 2 arguments for --outputs.")
151
152    prefix, filename = args
153    outputs = [posixpath.join(prefix, f)
154               for f in Outputs(filename, defines,
155                                options.ids_file, options.target_platform)]
156    return '\n'.join(outputs)
157  else:
158    raise WrongNumberOfArguments("Expected --inputs or --outputs.")
159
160
161def main(argv):
162  try:
163    result = DoMain(argv[1:])
164  except WrongNumberOfArguments as e:
165    PrintUsage()
166    print(e)
167    return 1
168  print(result)
169  return 0
170
171
172if __name__ == '__main__':
173  sys.exit(main(sys.argv))
174