| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 '''The 'grit build' tool along with integration for this tool with the | 6 '''The 'grit build' tool along with integration for this tool with the |
| 7 SCons build system. | 7 SCons build system. |
| 8 ''' | 8 ''' |
| 9 | 9 |
| 10 import filecmp | 10 import filecmp |
| 11 import getopt | 11 import getopt |
| 12 import os | 12 import os |
| 13 import shutil | 13 import shutil |
| 14 import sys | 14 import sys |
| 15 import types | 15 import types |
| 16 | 16 |
| 17 from grit import grd_reader | 17 from grit import grd_reader |
| 18 from grit import util | 18 from grit import util |
| 19 from grit.tool import interface | 19 from grit.tool import interface |
| 20 from grit import shortcuts | 20 from grit import shortcuts |
| 21 | 21 |
| 22 | 22 |
| 23 def ParseDefine(define): | |
| 24 '''Parses a define that is either like "NAME" or "NAME=VAL" and | |
| 25 returns its components, using True as the default value. Values of | |
| 26 "1" and "0" are transformed to True and False respectively. | |
| 27 ''' | |
| 28 parts = [part.strip() for part in define.split('=')] | |
| 29 assert len(parts) >= 1 | |
| 30 name = parts[0] | |
| 31 val = True | |
| 32 if len(parts) > 1: | |
| 33 val = parts[1] | |
| 34 if val == "1": val = True | |
| 35 elif val == "0": val = False | |
| 36 return (name, val) | |
| 37 | |
| 38 | |
| 39 class RcBuilder(interface.Tool): | 23 class RcBuilder(interface.Tool): |
| 40 '''A tool that builds RC files and resource header files for compilation. | 24 '''A tool that builds RC files and resource header files for compilation. |
| 41 | 25 |
| 42 Usage: grit build [-o OUTPUTDIR] [-D NAME[=VAL]]* | 26 Usage: grit build [-o OUTPUTDIR] [-D NAME[=VAL]]* |
| 43 | 27 |
| 44 All output options for this tool are specified in the input file (see | 28 All output options for this tool are specified in the input file (see |
| 45 'grit help' for details on how to specify the input file - it is a global | 29 'grit help' for details on how to specify the input file - it is a global |
| 46 option). | 30 option). |
| 47 | 31 |
| 48 Options: | 32 Options: |
| (...skipping 28 matching lines...) Expand all Loading... |
| 77 | 61 |
| 78 def Run(self, opts, args): | 62 def Run(self, opts, args): |
| 79 self.output_directory = '.' | 63 self.output_directory = '.' |
| 80 first_ids_file = None | 64 first_ids_file = None |
| 81 whitelist_filenames = [] | 65 whitelist_filenames = [] |
| 82 (own_opts, args) = getopt.getopt(args, 'o:D:E:f:w:') | 66 (own_opts, args) = getopt.getopt(args, 'o:D:E:f:w:') |
| 83 for (key, val) in own_opts: | 67 for (key, val) in own_opts: |
| 84 if key == '-o': | 68 if key == '-o': |
| 85 self.output_directory = val | 69 self.output_directory = val |
| 86 elif key == '-D': | 70 elif key == '-D': |
| 87 name, val = ParseDefine(val) | 71 name, val = util.ParseDefine(val) |
| 88 self.defines[name] = val | 72 self.defines[name] = val |
| 89 elif key == '-E': | 73 elif key == '-E': |
| 90 (env_name, env_value) = val.split('=') | 74 (env_name, env_value) = val.split('=') |
| 91 os.environ[env_name] = env_value | 75 os.environ[env_name] = env_value |
| 92 elif key == '-f': | 76 elif key == '-f': |
| 93 # TODO(joi@chromium.org): Remove this override once change | 77 # TODO(joi@chromium.org): Remove this override once change |
| 94 # lands in WebKit.grd to specify the first_ids_file in the | 78 # lands in WebKit.grd to specify the first_ids_file in the |
| 95 # .grd itself. | 79 # .grd itself. |
| 96 first_ids_file = val | 80 first_ids_file = val |
| 97 elif key == '-w': | 81 elif key == '-w': |
| 98 whitelist_filenames.append(val) | 82 whitelist_filenames.append(val) |
| 99 | 83 |
| 100 if len(args): | 84 if len(args): |
| 101 print "This tool takes no tool-specific arguments." | 85 print 'This tool takes no tool-specific arguments.' |
| 102 return 2 | 86 return 2 |
| 103 self.SetOptions(opts) | 87 self.SetOptions(opts) |
| 104 if self.scons_targets: | 88 if self.scons_targets: |
| 105 self.VerboseOut('Using SCons targets to identify files to output.\n') | 89 self.VerboseOut('Using SCons targets to identify files to output.\n') |
| 106 else: | 90 else: |
| 107 self.VerboseOut('Output directory: %s (absolute path: %s)\n' % | 91 self.VerboseOut('Output directory: %s (absolute path: %s)\n' % |
| 108 (self.output_directory, | 92 (self.output_directory, |
| 109 os.path.abspath(self.output_directory))) | 93 os.path.abspath(self.output_directory))) |
| 110 | 94 |
| 111 if whitelist_filenames: | 95 if whitelist_filenames: |
| 112 self.whitelist_names = set() | 96 self.whitelist_names = set() |
| 113 for whitelist_filename in whitelist_filenames: | 97 for whitelist_filename in whitelist_filenames: |
| 114 self.VerboseOut('Using whitelist: %s\n' % whitelist_filename); | 98 self.VerboseOut('Using whitelist: %s\n' % whitelist_filename); |
| 115 whitelist_file = open(whitelist_filename) | 99 whitelist_file = open(whitelist_filename) |
| 116 self.whitelist_names |= set(whitelist_file.read().strip().split('\n')) | 100 self.whitelist_names |= set(whitelist_file.read().strip().split('\n')) |
| 117 whitelist_file.close() | 101 whitelist_file.close() |
| 118 | 102 |
| 119 self.res = grd_reader.Parse(opts.input, | 103 self.res = grd_reader.Parse(opts.input, |
| 120 debug=opts.extra_verbose, | 104 debug=opts.extra_verbose, |
| 121 first_ids_file=first_ids_file, | 105 first_ids_file=first_ids_file, |
| 122 defines=self.defines) | 106 defines=self.defines) |
| 123 # Set an output context so that conditionals can use defines during the | 107 # Set an output context so that conditionals can use defines during the |
| 124 # gathering stage; we use a dummy language here since we are not outputting | 108 # gathering stage; we use a dummy language here since we are not outputting |
| 125 # a specific language. | 109 # a specific language. |
| 126 self.res.SetOutputContext('no-specific-language', self.defines) | 110 self.res.SetOutputContext('en', self.defines) |
| 127 self.res.RunGatherers(recursive = True) | 111 self.res.RunGatherers(recursive = True) |
| 128 self.Process() | 112 self.Process() |
| 129 return 0 | 113 return 0 |
| 130 | 114 |
| 131 def __init__(self, defines=None): | 115 def __init__(self, defines=None): |
| 132 # Default file-creation function is built-in file(). Only done to allow | 116 # Default file-creation function is built-in file(). Only done to allow |
| 133 # overriding by unit test. | 117 # overriding by unit test. |
| 134 self.fo_create = file | 118 self.fo_create = file |
| 135 | 119 |
| 136 # key/value pairs of C-preprocessor like defines that are used for | 120 # key/value pairs of C-preprocessor like defines that are used for |
| 137 # conditional output of resources | 121 # conditional output of resources |
| 138 if defines: | 122 self.defines = defines or {} |
| 139 self.defines = defines | |
| 140 else: | |
| 141 self.defines = {} | |
| 142 | 123 |
| 143 # self.res is a fully-populated resource tree if Run() | 124 # self.res is a fully-populated resource tree if Run() |
| 144 # has been called, otherwise None. | 125 # has been called, otherwise None. |
| 145 self.res = None | 126 self.res = None |
| 146 | 127 |
| 147 # Set to a list of filenames for the output nodes that are relative | 128 # Set to a list of filenames for the output nodes that are relative |
| 148 # to the current working directory. They are in the same order as the | 129 # to the current working directory. They are in the same order as the |
| 149 # output nodes in the file. | 130 # output nodes in the file. |
| 150 self.scons_targets = None | 131 self.scons_targets = None |
| 151 | 132 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 # Note: Some Format calls have side effects, so Format is always called | 166 # Note: Some Format calls have side effects, so Format is always called |
| 186 # and the whitelist is used to only avoid the output. | 167 # and the whitelist is used to only avoid the output. |
| 187 should_write = not node.WhitelistMarkedAsSkip() | 168 should_write = not node.WhitelistMarkedAsSkip() |
| 188 | 169 |
| 189 base_dir = util.dirname(output_node.GetOutputFilename()) | 170 base_dir = util.dirname(output_node.GetOutputFilename()) |
| 190 | 171 |
| 191 try: | 172 try: |
| 192 formatter = node.ItemFormatter(output_node.GetType()) | 173 formatter = node.ItemFormatter(output_node.GetType()) |
| 193 if formatter: | 174 if formatter: |
| 194 formatted = formatter.Format(node, output_node.GetLanguage(), | 175 formatted = formatter.Format(node, output_node.GetLanguage(), |
| 195 begin_item=True, output_dir=base_dir) | 176 output_dir=base_dir) |
| 196 if should_write: | 177 if should_write: |
| 197 outfile.write(formatted) | 178 outfile.write(formatted) |
| 198 except: | 179 except: |
| 199 print u"Error processing node %s" % unicode(node) | 180 print u'Error processing node %s' % unicode(node) |
| 200 raise | 181 raise |
| 201 | 182 |
| 202 for child in node.children: | 183 for child in node.children: |
| 203 RcBuilder.ProcessNode(child, output_node, outfile) | 184 RcBuilder.ProcessNode(child, output_node, outfile) |
| 204 | 185 |
| 205 try: | 186 try: |
| 206 if formatter: | 187 if formatter: |
| 207 formatted = formatter.Format(node, output_node.GetLanguage(), | 188 formatted = formatter.FormatEnd(node, output_node.GetLanguage(), |
| 208 begin_item=False, output_dir=base_dir) | 189 output_dir=base_dir) |
| 209 if should_write: | 190 if should_write: |
| 210 outfile.write(formatted) | 191 outfile.write(formatted) |
| 211 except: | 192 except: |
| 212 print u"Error processing node %s" % unicode(node) | 193 print u'Error processing node %s' % unicode(node) |
| 213 raise | 194 raise |
| 214 ProcessNode = staticmethod(ProcessNode) | 195 ProcessNode = staticmethod(ProcessNode) |
| 215 | 196 |
| 216 | 197 |
| 217 def Process(self): | 198 def Process(self): |
| 218 # Update filenames with those provided by SCons if we're being invoked | 199 # Update filenames with those provided by SCons if we're being invoked |
| 219 # from SCons. The list of SCons targets also includes all <structure> | 200 # from SCons. The list of SCons targets also includes all <structure> |
| 220 # node outputs, but it starts with our output files, in the order they | 201 # node outputs, but it starts with our output files, in the order they |
| 221 # occur in the .grd | 202 # occur in the .grd |
| 222 if self.scons_targets: | 203 if self.scons_targets: |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 303 | 284 |
| 304 # Print out any fallback warnings, and missing translation errors, and | 285 # Print out any fallback warnings, and missing translation errors, and |
| 305 # exit with an error code if there are missing translations in a non-pseudo | 286 # exit with an error code if there are missing translations in a non-pseudo |
| 306 # and non-official build. | 287 # and non-official build. |
| 307 warnings = (self.res.UberClique().MissingTranslationsReport(). | 288 warnings = (self.res.UberClique().MissingTranslationsReport(). |
| 308 encode('ascii', 'replace')) | 289 encode('ascii', 'replace')) |
| 309 if warnings and self.defines.get('_google_chrome', False): | 290 if warnings and self.defines.get('_google_chrome', False): |
| 310 print warnings | 291 print warnings |
| 311 if self.res.UberClique().HasMissingTranslations(): | 292 if self.res.UberClique().HasMissingTranslations(): |
| 312 sys.exit(-1) | 293 sys.exit(-1) |
| OLD | NEW |