fstat(): Return a different st_ino each time instead of a fixed value
[nativeclient.git] / site_scons / usage_log.py
blob80ccc1643eb88a4ccb60d631eb00add76ef0821f
1 #!/usr/bin/python2.4
2 # Copyright 2009, Google Inc.
3 # All rights reserved.
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 # * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 """Optional usage logging for Software Construction Toolkit."""
33 import atexit
34 import os
36 # GOOGLE_CHANGE(rspangler) - FROM THIS:
37 # import platform
38 # GOOGLE_CHANGE(rspangler) - END CHANGE.
39 import sys
40 import time
41 import xml.dom
42 import SCons
43 import SCons.Script
46 chain_build_targets = None # Previous SCons _build_targets function
48 #------------------------------------------------------------------------------
49 # Wrappers and hooks into SCons
52 class ProgressDisplayWrapper(object):
53 """Wrapper around SCons.Util.DisplayEngine.
55 Needs to be has-a not is-a, since DisplayEngine.set_mode() overrides the
56 __call__ member.
57 """
59 def __init__(self, old_display):
60 """Constructor.
62 Args:
63 old_display: Old display object to chain to.
64 """
65 self.old_display = old_display
67 def __call__(self, text, append_newline=1):
68 """Display progress.
70 Args:
71 text: Text to display.
72 append_newline: Append newline to text if non-zero.
74 Returns:
75 Passthru from old display object.
76 """
77 log.AddEntry('progress %s' % text)
78 return self.old_display(text, append_newline)
80 def set_mode(self, mode):
81 """Passthru to DisplayEngine.setmode().
83 Args:
84 mode: If non-zero, print progress.
86 Returns:
87 Passthru from old display object.
88 """
89 return self.old_display.set_mode(mode)
92 def BuildTargetsWrapper(fs, options, targets, target_top):
93 """Wrapper around SCons.Script.Main._build_targets().
95 Args:
96 fs: Filesystem object.
97 options: SCons options (after modification by SConscripts.
98 targets: Targets to build.
99 target_top: Passed through to _build_targets().
101 log.AddEntry('build_targets start')
102 log.SetParam('build_targets.targets', map(str, targets))
104 # Get list of non-default options. SConscript settings override defaults.
105 build_opts = dict(options.__SConscript_settings__)
106 # Command line settings are direct attrs, and override SConscript settings.
107 for key in dir(options):
108 if key.startswith('__') or key == 'settable':
109 continue
110 value = getattr(options, key)
111 if callable(value):
112 continue
113 build_opts[key] = value
115 for key, value in build_opts.items():
116 log.SetParam('build_targets.option.%s' % key, value)
118 try:
119 returnval = None
120 if chain_build_targets:
121 returnval = chain_build_targets(fs, options, targets, target_top)
122 return returnval
123 finally:
124 log.AddEntry('build_targets done')
127 def PrecmdWrapper(self, line):
128 """Pre-command handler for SCons.Script.Interactive() to support logging.
130 Args:
131 self: cmd object.
132 line: Command line which will be executed.
134 Returns:
135 Passthru value of line.
137 log.AddEntry('Interactive start')
138 log.SetParam('interactive.command', line or self.lastcmd)
139 return line
142 def PostcmdWrapper(self, stop, line):
143 """Post-command handler for SCons.Script.Interactive() to support logging.
145 Args:
146 self: cmd object.
147 stop: Will execution stop after this function exits?
148 line: Command line which was executed.
150 Returns:
151 Passthru value of stop.
153 log.AddEntry('Interactive done')
154 log.Dump()
155 return stop
158 #------------------------------------------------------------------------------
159 # Usage log object
162 class Log(object):
163 """Usage log object."""
165 def __init__(self):
166 """Constructor."""
167 self.params = {}
168 self.entries = []
169 self.dump_writer = None
170 self.time = time.time
172 def SetParam(self, key, value):
173 """Sets a parameter.
175 Args:
176 key: Parameter name (string).
177 value: Value for parameter.
179 self.params[key] = value
181 def AddEntry(self, text):
182 """Adds a timestamped log entry.
184 Args:
185 text: Text of log entry.
187 self.entries.append((self.time(), text))
189 def ConvertToXml(self):
190 """Converts the usage log to XML.
192 Returns:
193 An xml.dom.minidom.Document object with the usage log contents.
195 xml_impl = xml.dom.getDOMImplementation()
196 xml_doc = xml_impl.createDocument(None, 'usage_log', None)
198 # List build params
199 xml_param_list = xml_doc.createElement('param_list')
200 xml_doc.documentElement.appendChild(xml_param_list)
201 for key in sorted(self.params):
202 xml_param = xml_doc.createElement('param')
203 xml_param.setAttribute('name', str(key))
204 xml_param_list.appendChild(xml_param)
206 value = self.params[key]
207 if hasattr(value, '__iter__'):
208 # Iterable value, so list items
209 for v in value:
210 xml_item = xml_doc.createElement('item')
211 xml_item.setAttribute('value', str(v))
212 xml_param.appendChild(xml_item)
213 else:
214 # Non-iterable, so convert to string
215 xml_param.setAttribute('value', str(value))
217 # List log entries
218 xml_entry_list = xml_doc.createElement('entry_list')
219 xml_doc.documentElement.appendChild(xml_entry_list)
220 for entry_time, entry_text in self.entries:
221 xml_entry = xml_doc.createElement('entry')
222 xml_entry.setAttribute('time', str(entry_time))
223 xml_entry.setAttribute('text', str(entry_text))
224 xml_entry_list.appendChild(xml_entry)
226 return xml_doc
228 def Dump(self):
229 """Dumps the log by calling self.dump_writer(), then clears the log."""
230 if self.dump_writer:
231 self.dump_writer(self)
233 # Clear log entries (but not params, since they can be used again if SCons
234 # is in interactive mode).
235 self.entries = []
238 def SetOutputFile(self, filename):
239 """Sets the output filename for usage log dumps.
241 Args:
242 filename: Name of output file.
244 self.dump_to_file = filename
245 self.dump_writer = FileDumpWriter
247 #------------------------------------------------------------------------------
248 # Usage log methods
250 def AddSystemParams():
251 """Prints system stats."""
252 log.SetParam('sys.argv', sys.argv)
253 log.SetParam('sys.executable', sys.executable)
254 log.SetParam('sys.version', sys.version)
255 log.SetParam('sys.version_info', sys.version_info)
256 log.SetParam('sys.path', sys.path)
257 log.SetParam('sys.platform', sys.platform)
258 # GOOGLE_CHANGE(rspangler) - FROM THIS:
259 # log.SetParam('platform.uname', platform.uname())
260 # log.SetParam('platform.platform', platform.platform())
261 # GOOGLE_CHANGE(rspangler) - END CHANGE.
263 for e in ['PATH', 'INCLUDE', 'LIB', 'HAMMER_OPTS', 'HAMMER_XGE']:
264 log.SetParam('shell.%s' % e, os.environ.get(e, ''))
266 log.SetParam('scons.version', SCons.__version__)
269 def AtExit():
270 """Usage log cleanup at exit."""
271 log.AddEntry('usage_log exit')
272 log.Dump()
275 def AtModuleLoad():
276 """Code executed at module load time."""
277 AddSystemParams()
279 # Wrap SCons' progress display wrapper
280 SCons.Script.Main.progress_display = ProgressDisplayWrapper(
281 SCons.Script.Main.progress_display)
283 # Wrap SCons' _build_targets()
284 global chain_build_targets
285 chain_build_targets = SCons.Script.Main._build_targets
286 SCons.Script.Main._build_targets = BuildTargetsWrapper
288 # Hook SCons interactive mode
289 SCons.Script.Interactive.SConsInteractiveCmd.precmd = PrecmdWrapper
290 SCons.Script.Interactive.SConsInteractiveCmd.postcmd = PostcmdWrapper
292 # Make sure we get called at exit
293 atexit.register(AtExit)
296 def FileDumpWriter(log):
297 """Dumps the log to the specified file."""
298 print 'Writing usage log to %s...' % log.dump_to_file
299 f = open(log.dump_to_file, 'wt')
300 doc = log.ConvertToXml()
301 doc.writexml(f, encoding='UTF-8', addindent=' ', newl='\n')
302 doc.unlink()
303 f.close()
304 print 'Done writing log.'
307 # Create the initial log (can't do this in AtModuleLoad() without 'global')
308 log = Log()
309 log.AddEntry('usage_log loaded')
311 # Do other work at module load time
312 AtModuleLoad()