| OLD | NEW |
| (Empty) |
| 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 '''Class for reading GRD files into memory, without processing them. | |
| 7 ''' | |
| 8 | |
| 9 import os.path | |
| 10 import types | |
| 11 import xml.sax | |
| 12 import xml.sax.handler | |
| 13 | |
| 14 from grit import exception | |
| 15 from grit import util | |
| 16 from grit.node import base | |
| 17 from grit.node import mapping | |
| 18 from grit.node import misc | |
| 19 | |
| 20 | |
| 21 class StopParsingException(Exception): | |
| 22 '''An exception used to stop parsing.''' | |
| 23 pass | |
| 24 | |
| 25 | |
| 26 class GrdContentHandler(xml.sax.handler.ContentHandler): | |
| 27 def __init__(self, stop_after, debug, dir, defines, tags_to_ignore, | |
| 28 target_platform): | |
| 29 # Invariant of data: | |
| 30 # 'root' is the root of the parse tree being created, or None if we haven't | |
| 31 # parsed out any elements. | |
| 32 # 'stack' is the a stack of elements that we push new nodes onto and | |
| 33 # pop from when they finish parsing, or [] if we are not currently parsing. | |
| 34 # 'stack[-1]' is the top of the stack. | |
| 35 self.root = None | |
| 36 self.stack = [] | |
| 37 self.stop_after = stop_after | |
| 38 self.debug = debug | |
| 39 self.dir = dir | |
| 40 self.defines = defines | |
| 41 self.tags_to_ignore = tags_to_ignore or set() | |
| 42 self.ignore_depth = 0 | |
| 43 self.target_platform = target_platform | |
| 44 | |
| 45 def startElement(self, name, attrs): | |
| 46 if self.ignore_depth or name in self.tags_to_ignore: | |
| 47 if self.debug and self.ignore_depth == 0: | |
| 48 print "Ignoring element %s and its children" % name | |
| 49 self.ignore_depth += 1 | |
| 50 return | |
| 51 | |
| 52 if self.debug: | |
| 53 attr_list = ' '.join('%s="%s"' % kv for kv in attrs.items()) | |
| 54 print ("Starting parsing of element %s with attributes %r" % | |
| 55 (name, attr_list or '(none)')) | |
| 56 | |
| 57 typeattr = attrs.get('type') | |
| 58 node = mapping.ElementToClass(name, typeattr)() | |
| 59 | |
| 60 if self.stack: | |
| 61 self.stack[-1].AddChild(node) | |
| 62 node.StartParsing(name, self.stack[-1]) | |
| 63 else: | |
| 64 assert self.root is None | |
| 65 self.root = node | |
| 66 if isinstance(self.root, misc.GritNode): | |
| 67 if self.target_platform: | |
| 68 self.root.SetTargetPlatform(self.target_platform) | |
| 69 node.StartParsing(name, None) | |
| 70 if self.defines: | |
| 71 node.SetDefines(self.defines) | |
| 72 self.stack.append(node) | |
| 73 | |
| 74 for attr, attrval in attrs.items(): | |
| 75 node.HandleAttribute(attr, attrval) | |
| 76 | |
| 77 def endElement(self, name): | |
| 78 if self.ignore_depth: | |
| 79 self.ignore_depth -= 1 | |
| 80 return | |
| 81 | |
| 82 if name == 'part': | |
| 83 partnode = self.stack[-1] | |
| 84 partnode.started_inclusion = True | |
| 85 # Add the contents of the sub-grd file as children of the <part> node. | |
| 86 partname = partnode.GetInputPath() | |
| 87 if os.path.dirname(partname): | |
| 88 # TODO(benrg): Remove this limitation. (The problem is that GRIT | |
| 89 # assumes that files referenced from the GRD file are relative to | |
| 90 # a path stored in the root <grit> node.) | |
| 91 raise exception.GotPathExpectedFilenameOnly() | |
| 92 partname = os.path.join(self.dir, partname) | |
| 93 # Exceptions propagate to the handler in grd_reader.Parse(). | |
| 94 xml.sax.parse(partname, GrdPartContentHandler(self)) | |
| 95 | |
| 96 if self.debug: | |
| 97 print "End parsing of element %s" % name | |
| 98 self.stack.pop().EndParsing() | |
| 99 | |
| 100 if name == self.stop_after: | |
| 101 raise StopParsingException() | |
| 102 | |
| 103 def characters(self, content): | |
| 104 if self.ignore_depth == 0: | |
| 105 if self.stack[-1]: | |
| 106 self.stack[-1].AppendContent(content) | |
| 107 | |
| 108 def ignorableWhitespace(self, whitespace): | |
| 109 # TODO(joi) This is not supported by expat. Should use a different XML par
ser? | |
| 110 pass | |
| 111 | |
| 112 | |
| 113 class GrdPartContentHandler(xml.sax.handler.ContentHandler): | |
| 114 def __init__(self, parent): | |
| 115 self.parent = parent | |
| 116 self.depth = 0 | |
| 117 | |
| 118 def startElement(self, name, attrs): | |
| 119 if self.depth: | |
| 120 self.parent.startElement(name, attrs) | |
| 121 else: | |
| 122 if name != 'grit-part': | |
| 123 raise exception.MissingElement("root tag must be <grit-part>") | |
| 124 if attrs: | |
| 125 raise exception.UnexpectedAttribute( | |
| 126 "<grit-part> tag must not have attributes") | |
| 127 self.depth += 1 | |
| 128 | |
| 129 def endElement(self, name): | |
| 130 self.depth -= 1 | |
| 131 if self.depth: | |
| 132 self.parent.endElement(name) | |
| 133 | |
| 134 def characters(self, content): | |
| 135 self.parent.characters(content) | |
| 136 | |
| 137 def ignorableWhitespace(self, whitespace): | |
| 138 self.parent.ignorableWhitespace(whitespace) | |
| 139 | |
| 140 | |
| 141 def Parse(filename_or_stream, dir=None, stop_after=None, first_ids_file=None, | |
| 142 debug=False, defines=None, tags_to_ignore=None, target_platform=None): | |
| 143 '''Parses a GRD file into a tree of nodes (from grit.node). | |
| 144 | |
| 145 If filename_or_stream is a stream, 'dir' should point to the directory | |
| 146 notionally containing the stream (this feature is only used in unit tests). | |
| 147 | |
| 148 If 'stop_after' is provided, the parsing will stop once the first node | |
| 149 with this name has been fully parsed (including all its contents). | |
| 150 | |
| 151 If 'debug' is true, lots of information about the parsing events will be | |
| 152 printed out during parsing of the file. | |
| 153 | |
| 154 If 'first_ids_file' is non-empty, it is used to override the setting for the | |
| 155 first_ids_file attribute of the <grit> root node. Note that the first_ids_file | |
| 156 parameter should be relative to the cwd, even though the first_ids_file | |
| 157 attribute of the <grit> node is relative to the grd file. | |
| 158 | |
| 159 If 'target_platform' is set, this is used to determine the target | |
| 160 platform of builds, instead of using |sys.platform|. | |
| 161 | |
| 162 Args: | |
| 163 filename_or_stream: './bla.xml' | |
| 164 dir: None (if filename_or_stream is a filename) or '.' | |
| 165 stop_after: 'inputs' | |
| 166 first_ids_file: 'GRIT_DIR/../gritsettings/resource_ids' | |
| 167 debug: False | |
| 168 defines: dictionary of defines, like {'chromeos': '1'} | |
| 169 target_platform: None or the value that would be returned by sys.platform | |
| 170 on your target platform. | |
| 171 | |
| 172 Return: | |
| 173 Subclass of grit.node.base.Node | |
| 174 | |
| 175 Throws: | |
| 176 grit.exception.Parsing | |
| 177 ''' | |
| 178 | |
| 179 if dir is None and isinstance(filename_or_stream, types.StringType): | |
| 180 dir = util.dirname(filename_or_stream) | |
| 181 | |
| 182 handler = GrdContentHandler(stop_after=stop_after, debug=debug, dir=dir, | |
| 183 defines=defines, tags_to_ignore=tags_to_ignore, | |
| 184 target_platform=target_platform) | |
| 185 try: | |
| 186 xml.sax.parse(filename_or_stream, handler) | |
| 187 except StopParsingException: | |
| 188 assert stop_after | |
| 189 pass | |
| 190 except: | |
| 191 if not debug: | |
| 192 print "parse exception: run GRIT with the -x flag to debug .grd problems" | |
| 193 raise | |
| 194 | |
| 195 if handler.root.name != 'grit': | |
| 196 raise exception.MissingElement("root tag must be <grit>") | |
| 197 | |
| 198 if hasattr(handler.root, 'SetOwnDir'): | |
| 199 # Fix up the base_dir so it is relative to the input file. | |
| 200 assert dir is not None | |
| 201 handler.root.SetOwnDir(dir) | |
| 202 | |
| 203 if isinstance(handler.root, misc.GritNode): | |
| 204 if first_ids_file: | |
| 205 # Make the path to the first_ids_file relative to the grd file, | |
| 206 # unless it begins with GRIT_DIR. | |
| 207 GRIT_DIR_PREFIX = 'GRIT_DIR' | |
| 208 if not (first_ids_file.startswith(GRIT_DIR_PREFIX) | |
| 209 and first_ids_file[len(GRIT_DIR_PREFIX)] in ['/', '\\']): | |
| 210 rel_dir = os.path.relpath(os.getcwd(), dir) | |
| 211 first_ids_file = util.normpath(os.path.join(rel_dir, first_ids_file)) | |
| 212 handler.root.attrs['first_ids_file'] = first_ids_file | |
| 213 # Assign first ids to the nodes that don't have them. | |
| 214 handler.root.AssignFirstIds(filename_or_stream, defines) | |
| 215 | |
| 216 return handler.root | |
| 217 | |
| 218 | |
| 219 if __name__ == '__main__': | |
| 220 util.ChangeStdoutEncoding() | |
| 221 print unicode(Parse(sys.argv[1])) | |
| OLD | NEW |