Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: grit/tool/build.py

Issue 7994004: Initial source commit to grit-i18n project. (Closed) Base URL: http://grit-i18n.googlecode.com/svn/trunk/
Patch Set: Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « grit/tool/__init__.py ('k') | grit/tool/count.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 #!/usr/bin/python2.4
2 # Copyright (c) 2011 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 '''The 'grit build' tool along with integration for this tool with the
7 SCons build system.
8 '''
9
10 import filecmp
11 import getopt
12 import os
13 import shutil
14 import sys
15 import types
16
17 from grit import grd_reader
18 from grit import util
19 from grit.tool import interface
20 from grit import shortcuts
21
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):
40 '''A tool that builds RC files and resource header files for compilation.
41
42 Usage: grit build [-o OUTPUTDIR] [-D NAME[=VAL]]*
43
44 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
46 option).
47
48 Options:
49
50 -o OUTPUTDIR Specify what directory output paths are relative to.
51 Defaults to the current directory.
52
53 -D NAME[=VAL] Specify a C-preprocessor-like define NAME with optional
54 value VAL (defaults to 1) which will be used to control
55 conditional inclusion of resources.
56
57 -E NAME=VALUE Set environment variable NAME to VALUE (within grit).
58
59 -f FIRSTIDFILE Path to a python file that specifies the first id of
60 value to use for resources. Defaults to the file
61 resources_ids next to grit.py. Set to an empty string
62 if you don't want to use a first id file.
63
64 -w WHITELISTFILE Path to a file containing the string names of the
65 resources to include. Anything not listed is dropped.
66
67
68 Conditional inclusion of resources only affects the output of files which
69 control which resources get linked into a binary, e.g. it affects .rc files
70 meant for compilation but it does not affect resource header files (that define
71 IDs). This helps ensure that values of IDs stay the same, that all messages
72 are exported to translation interchange files (e.g. XMB files), etc.
73 '''
74
75 def ShortDescription(self):
76 return 'A tool that builds RC files for compilation.'
77
78 def Run(self, opts, args):
79 self.output_directory = '.'
80 first_id_filename = None
81 whitelist_filenames = []
82 (own_opts, args) = getopt.getopt(args, 'o:D:E:f:w:')
83 for (key, val) in own_opts:
84 if key == '-o':
85 self.output_directory = val
86 elif key == '-D':
87 name, val = ParseDefine(val)
88 self.defines[name] = val
89 elif key == '-E':
90 (env_name, env_value) = val.split('=')
91 os.environ[env_name] = env_value
92 elif key == '-f':
93 first_id_filename = val
94 elif key == '-w':
95 whitelist_filenames.append(val)
96
97 if len(args):
98 print "This tool takes no tool-specific arguments."
99 return 2
100 self.SetOptions(opts)
101 if self.scons_targets:
102 self.VerboseOut('Using SCons targets to identify files to output.\n')
103 else:
104 self.VerboseOut('Output directory: %s (absolute path: %s)\n' %
105 (self.output_directory,
106 os.path.abspath(self.output_directory)))
107
108 if whitelist_filenames:
109 self.whitelist_names = set()
110 for whitelist_filename in whitelist_filenames:
111 self.VerboseOut('Using whitelist: %s\n' % whitelist_filename);
112 whitelist_file = open(whitelist_filename)
113 self.whitelist_names |= set(whitelist_file.read().strip().split('\n'))
114 whitelist_file.close()
115
116 self.res = grd_reader.Parse(opts.input, first_id_filename=first_id_filename,
117 debug=opts.extra_verbose, defines=self.defines)
118 self.res.RunGatherers(recursive = True)
119 self.Process()
120 return 0
121
122 def __init__(self):
123 # Default file-creation function is built-in file(). Only done to allow
124 # overriding by unit test.
125 self.fo_create = file
126
127 # key/value pairs of C-preprocessor like defines that are used for
128 # conditional output of resources
129 self.defines = {}
130
131 # self.res is a fully-populated resource tree if Run()
132 # has been called, otherwise None.
133 self.res = None
134
135 # Set to a list of filenames for the output nodes that are relative
136 # to the current working directory. They are in the same order as the
137 # output nodes in the file.
138 self.scons_targets = None
139
140 # The set of names that are whitelisted to actually be included in the
141 # output.
142 self.whitelist_names = None
143
144
145 # static method
146 def AddWhitelistTags(start_node, whitelist_names):
147 # Walk the tree of nodes added attributes for the nodes that shouldn't
148 # be written into the target files (skip markers).
149 from grit.node import include
150 from grit.node import message
151 for node in start_node.inorder():
152 # Same trick data_pack.py uses to see what nodes actually result in
153 # real items.
154 if (isinstance(node, include.IncludeNode) or
155 isinstance(node, message.MessageNode)):
156 text_ids = node.GetTextualIds()
157 # Mark the item to be skipped if it wasn't in the whitelist.
158 if text_ids and not text_ids[0] in whitelist_names:
159 node.SetWhitelistMarkedAsSkip(True)
160 AddWhitelistTags = staticmethod(AddWhitelistTags)
161
162 # static method
163 def ProcessNode(node, output_node, outfile):
164 '''Processes a node in-order, calling its formatter before and after
165 recursing to its children.
166
167 Args:
168 node: grit.node.base.Node subclass
169 output_node: grit.node.io.File
170 outfile: open filehandle
171 '''
172 # See if the node should be skipped by a whitelist.
173 # Note: Some Format calls have side effects, so Format is always called
174 # and the whitelist is used to only avoid the output.
175 should_write = not node.WhitelistMarkedAsSkip()
176
177 base_dir = util.dirname(output_node.GetOutputFilename())
178
179 try:
180 formatter = node.ItemFormatter(output_node.GetType())
181 if formatter:
182 formatted = formatter.Format(node, output_node.GetLanguage(),
183 begin_item=True, output_dir=base_dir)
184 if should_write:
185 outfile.write(formatted)
186 except:
187 print u"Error processing node %s" % unicode(node)
188 raise
189
190 for child in node.children:
191 RcBuilder.ProcessNode(child, output_node, outfile)
192
193 try:
194 if formatter:
195 formatted = formatter.Format(node, output_node.GetLanguage(),
196 begin_item=False, output_dir=base_dir)
197 if should_write:
198 outfile.write(formatted)
199 except:
200 print u"Error processing node %s" % unicode(node)
201 raise
202 ProcessNode = staticmethod(ProcessNode)
203
204
205 def Process(self):
206 # Update filenames with those provided by SCons if we're being invoked
207 # from SCons. The list of SCons targets also includes all <structure>
208 # node outputs, but it starts with our output files, in the order they
209 # occur in the .grd
210 if self.scons_targets:
211 assert len(self.scons_targets) >= len(self.res.GetOutputFiles())
212 outfiles = self.res.GetOutputFiles()
213 for ix in range(len(outfiles)):
214 outfiles[ix].output_filename = os.path.abspath(
215 self.scons_targets[ix])
216 else:
217 for output in self.res.GetOutputFiles():
218 output.output_filename = os.path.abspath(os.path.join(
219 self.output_directory, output.GetFilename()))
220
221 # If there are whitelisted names, tag the tree once up front, this way
222 # while looping through the actual output, it is just an attribute check.
223 if self.whitelist_names:
224 self.AddWhitelistTags(self.res, self.whitelist_names)
225
226 for output in self.res.GetOutputFiles():
227 self.VerboseOut('Creating %s...' % output.GetFilename())
228
229 # Microsoft's RC compiler can only deal with single-byte or double-byte
230 # files (no UTF-8), so we make all RC files UTF-16 to support all
231 # character sets.
232 if output.GetType() in ('rc_header', 'resource_map_header',
233 'resource_map_source', 'resource_file_map_source'):
234 encoding = 'cp1252'
235 elif output.GetType() in ('js_map_format', 'plist', 'plist_strings',
236 'doc', 'json'):
237 encoding = 'utf_8'
238 else:
239 # TODO(gfeher) modify here to set utf-8 encoding for admx/adml
240 encoding = 'utf_16'
241
242 # Make the output directory if it doesn't exist.
243 outdir = os.path.split(output.GetOutputFilename())[0]
244 if not os.path.exists(outdir):
245 os.makedirs(outdir)
246 # Write the results to a temporary file and only overwrite the original
247 # if the file changed. This avoids unnecessary rebuilds.
248 outfile = self.fo_create(output.GetOutputFilename() + '.tmp', 'wb')
249
250 if output.GetType() != 'data_package':
251 outfile = util.WrapOutputStream(outfile, encoding)
252
253 # Set the context, for conditional inclusion of resources
254 self.res.SetOutputContext(output.GetLanguage(), self.defines)
255
256 # TODO(joi) Handle this more gracefully
257 import grit.format.rc_header
258 grit.format.rc_header.Item.ids_ = {}
259
260 # Iterate in-order through entire resource tree, calling formatters on
261 # the entry into a node and on exit out of it.
262 self.ProcessNode(self.res, output, outfile)
263 outfile.close()
264
265 # Now copy from the temp file back to the real output, but on Windows,
266 # only if the real output doesn't exist or the contents of the file
267 # changed. This prevents identical headers from being written and .cc
268 # files from recompiling (which is painful on Windows).
269 if not os.path.exists(output.GetOutputFilename()):
270 os.rename(output.GetOutputFilename() + '.tmp',
271 output.GetOutputFilename())
272 else:
273 # CHROMIUM SPECIFIC CHANGE.
274 # This clashes with gyp + vstudio, which expect the output timestamp
275 # to change on a rebuild, even if nothing has changed.
276 #files_match = filecmp.cmp(output.GetOutputFilename(),
277 # output.GetOutputFilename() + '.tmp')
278 #if (output.GetType() != 'rc_header' or not files_match
279 # or sys.platform != 'win32'):
280 shutil.copy2(output.GetOutputFilename() + '.tmp',
281 output.GetOutputFilename())
282 os.remove(output.GetOutputFilename() + '.tmp')
283
284 self.VerboseOut(' done.\n')
285
286 # Print warnings if there are any duplicate shortcuts.
287 warnings = shortcuts.GenerateDuplicateShortcutsWarnings(
288 self.res.UberClique(), self.res.GetTcProject())
289 if warnings:
290 print '\n'.join(warnings)
291
292 # Print out any fallback warnings, and missing translation errors, and
293 # exit with an error code if there are missing translations in a non-pseudo
294 # and non-official build.
295 warnings = (self.res.UberClique().MissingTranslationsReport().
296 encode('ascii', 'replace'))
297 if warnings and self.defines.get('_google_chrome', False):
298 print warnings
299 if self.res.UberClique().HasMissingTranslations():
300 sys.exit(-1)
OLDNEW
« no previous file with comments | « grit/tool/__init__.py ('k') | grit/tool/count.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698