tools/mach_commands.py

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 from __future__ import unicode_literals
michael@0 6
michael@0 7 import sys
michael@0 8 import os
michael@0 9 import stat
michael@0 10 import platform
michael@0 11 import urllib2
michael@0 12 import errno
michael@0 13
michael@0 14 from mach.decorators import (
michael@0 15 CommandArgument,
michael@0 16 CommandProvider,
michael@0 17 Command,
michael@0 18 )
michael@0 19
michael@0 20 from mozbuild.base import MachCommandBase
michael@0 21
michael@0 22
michael@0 23 @CommandProvider
michael@0 24 class SearchProvider(object):
michael@0 25 @Command('mxr', category='misc',
michael@0 26 description='Search for something in MXR.')
michael@0 27 @CommandArgument('term', nargs='+', help='Term(s) to search for.')
michael@0 28 def mxr(self, term):
michael@0 29 import webbrowser
michael@0 30 term = ' '.join(term)
michael@0 31 uri = 'https://mxr.mozilla.org/mozilla-central/search?string=%s' % term
michael@0 32 webbrowser.open_new_tab(uri)
michael@0 33
michael@0 34 @Command('dxr', category='misc',
michael@0 35 description='Search for something in DXR.')
michael@0 36 @CommandArgument('term', nargs='+', help='Term(s) to search for.')
michael@0 37 def dxr(self, term):
michael@0 38 import webbrowser
michael@0 39 term = ' '.join(term)
michael@0 40 uri = 'http://dxr.mozilla.org/search?tree=mozilla-central&q=%s' % term
michael@0 41 webbrowser.open_new_tab(uri)
michael@0 42
michael@0 43 @Command('mdn', category='misc',
michael@0 44 description='Search for something on MDN.')
michael@0 45 @CommandArgument('term', nargs='+', help='Term(s) to search for.')
michael@0 46 def mdn(self, term):
michael@0 47 import webbrowser
michael@0 48 term = ' '.join(term)
michael@0 49 uri = 'https://developer.mozilla.org/search?q=%s' % term
michael@0 50 webbrowser.open_new_tab(uri)
michael@0 51
michael@0 52 @Command('google', category='misc',
michael@0 53 description='Search for something on Google.')
michael@0 54 @CommandArgument('term', nargs='+', help='Term(s) to search for.')
michael@0 55 def google(self, term):
michael@0 56 import webbrowser
michael@0 57 term = ' '.join(term)
michael@0 58 uri = 'https://www.google.com/search?q=%s' % term
michael@0 59 webbrowser.open_new_tab(uri)
michael@0 60
michael@0 61 @Command('search', category='misc',
michael@0 62 description='Search for something on the Internets. '
michael@0 63 'This will open 3 new browser tabs and search for the term on Google, '
michael@0 64 'MDN, and MXR.')
michael@0 65 @CommandArgument('term', nargs='+', help='Term(s) to search for.')
michael@0 66 def search(self, term):
michael@0 67 self.google(term)
michael@0 68 self.mdn(term)
michael@0 69 self.mxr(term)
michael@0 70
michael@0 71
michael@0 72 class Interface(object):
michael@0 73 '''
michael@0 74 Represents an XPIDL interface, in what file it is defined, what it derives
michael@0 75 from, what its uuid is, and where in the source file the uuid is.
michael@0 76 '''
michael@0 77 def __init__(self, filename, production):
michael@0 78 import xpidl
michael@0 79 assert isinstance(production, xpidl.Interface)
michael@0 80 self.name = production.name
michael@0 81 self.base = production.base
michael@0 82 self.filename = filename
michael@0 83 self.uuid = production.attributes.uuid
michael@0 84 location = production.location
michael@0 85 data = location._lexdata
michael@0 86 attr_pos = data.rfind(b'[', 0, location._lexpos)
michael@0 87 # uuid is always lowercase, but actual file content may not be.
michael@0 88 self.uuid_pos = data[attr_pos:location._lexpos].lower() \
michael@0 89 .rfind(self.uuid) + attr_pos
michael@0 90
michael@0 91
michael@0 92 class InterfaceRegistry(object):
michael@0 93 '''
michael@0 94 Tracks XPIDL interfaces, and allow to search them by name and by the
michael@0 95 interface they derive from.
michael@0 96 '''
michael@0 97 def __init__(self):
michael@0 98 self.by_name = {}
michael@0 99 self.by_base = {}
michael@0 100
michael@0 101 def get_by_name(self, name):
michael@0 102 return self.by_name.get(name, [])
michael@0 103
michael@0 104 def get_by_base(self, base):
michael@0 105 return self.by_base.get(base, [])
michael@0 106
michael@0 107 def add(self, interface):
michael@0 108 l = self.by_name.setdefault(interface.name, [])
michael@0 109 l.append(interface)
michael@0 110 l = self.by_base.setdefault(interface.base, [])
michael@0 111 l.append(interface)
michael@0 112
michael@0 113
michael@0 114 class IDLUpdater(object):
michael@0 115 '''
michael@0 116 Updates interfaces uuids in IDL files.
michael@0 117 '''
michael@0 118 def __init__(self, interfaces):
michael@0 119 from mozpack.copier import FileRegistry
michael@0 120 self.interfaces = interfaces;
michael@0 121 self.registry = FileRegistry()
michael@0 122
michael@0 123 def add(self, name):
michael@0 124 for interface in self.interfaces.get_by_name(name):
michael@0 125 self._add(interface)
michael@0 126
michael@0 127 def _add(self, interface):
michael@0 128 from mozpack.files import GeneratedFile
michael@0 129 from uuid import uuid4
michael@0 130 path = interface.filename
michael@0 131 if not self.registry.contains(path):
michael@0 132 self.registry.add(path, GeneratedFile(open(path).read()))
michael@0 133 content = self.registry[path].content
michael@0 134 content = content[:interface.uuid_pos] + str(uuid4()) + \
michael@0 135 content[interface.uuid_pos + len(interface.uuid):]
michael@0 136 self.registry[path].content = content
michael@0 137
michael@0 138 # Recurse through all the interfaces deriving from this one
michael@0 139 for derived in self.interfaces.get_by_base(interface.name):
michael@0 140 self._add(derived)
michael@0 141
michael@0 142 def update(self):
michael@0 143 for p, f in self.registry:
michael@0 144 f.copy(p)
michael@0 145
michael@0 146
michael@0 147 @CommandProvider
michael@0 148 class UUIDProvider(object):
michael@0 149 @Command('uuid', category='misc',
michael@0 150 description='Generate a uuid.')
michael@0 151 @CommandArgument('--format', '-f', choices=['idl', 'cpp', 'c++'],
michael@0 152 help='Output format for the generated uuid.')
michael@0 153 def uuid(self, format=None):
michael@0 154 import uuid
michael@0 155 u = uuid.uuid4()
michael@0 156 if format in [None, 'idl']:
michael@0 157 print(u)
michael@0 158 if format is None:
michael@0 159 print('')
michael@0 160 if format in [None, 'cpp', 'c++']:
michael@0 161 u = u.hex
michael@0 162 print('{ 0x%s, 0x%s, 0x%s, \\' % (u[0:8], u[8:12], u[12:16]))
michael@0 163 pairs = tuple(map(lambda n: u[n:n+2], range(16, 32, 2)))
michael@0 164 print((' { ' + '0x%s, ' * 7 + '0x%s } }') % pairs)
michael@0 165
michael@0 166 @Command('update-uuids', category='misc',
michael@0 167 description='Update IDL files with new UUIDs.')
michael@0 168 @CommandArgument('--path', default='.',
michael@0 169 help='Base path under which uuids will be searched.')
michael@0 170 @CommandArgument('interfaces', nargs='+',
michael@0 171 help='Changed interfaces whose UUIDs need to be updated. ' +
michael@0 172 'Their descendants are updated as well.')
michael@0 173 def update_uuids(self, path, interfaces):
michael@0 174 import os
michael@0 175 import xpidl
michael@0 176 from mozpack.files import FileFinder
michael@0 177 import mozpack.path
michael@0 178 from tempfile import mkdtemp
michael@0 179
michael@0 180 finder = FileFinder(path, find_executables=False)
michael@0 181 # Avoid creating xpidllex and xpidlyacc in the current directory.
michael@0 182 tmpdir = mkdtemp()
michael@0 183 try:
michael@0 184 parser = xpidl.IDLParser(outputdir=tmpdir)
michael@0 185 registry = InterfaceRegistry()
michael@0 186 for p, f in finder.find('**/*.idl'):
michael@0 187 p = mozpack.path.join(path, p)
michael@0 188 try:
michael@0 189 content = f.open().read()
michael@0 190 idl = parser.parse(content, filename=p)
michael@0 191 except Exception:
michael@0 192 continue
michael@0 193 for prod in idl.productions:
michael@0 194 if isinstance(prod, xpidl.Interface):
michael@0 195 registry.add(Interface(p, prod))
michael@0 196 finally:
michael@0 197 import shutil
michael@0 198 shutil.rmtree(tmpdir)
michael@0 199
michael@0 200 updates = IDLUpdater(registry)
michael@0 201
michael@0 202 for interface in interfaces:
michael@0 203 updates.add(interface)
michael@0 204
michael@0 205 updates.update()
michael@0 206
michael@0 207 @CommandProvider
michael@0 208 class PastebinProvider(object):
michael@0 209 @Command('pastebin', category='misc',
michael@0 210 description='Command line interface to pastebin.mozilla.org.')
michael@0 211 @CommandArgument('--language', default=None,
michael@0 212 help='Language to use for syntax highlighting')
michael@0 213 @CommandArgument('--poster', default=None,
michael@0 214 help='Specify your name for use with pastebin.mozilla.org')
michael@0 215 @CommandArgument('--duration', default='day',
michael@0 216 choices=['d', 'day', 'm', 'month', 'f', 'forever'],
michael@0 217 help='Keep for specified duration (default: %(default)s)')
michael@0 218 @CommandArgument('file', nargs='?', default=None,
michael@0 219 help='Specify the file to upload to pastebin.mozilla.org')
michael@0 220
michael@0 221 def pastebin(self, language, poster, duration, file):
michael@0 222 import sys
michael@0 223 import urllib
michael@0 224
michael@0 225 URL = 'http://pastebin.mozilla.org/'
michael@0 226
michael@0 227 FILE_TYPES = [{'value': 'text', 'name': 'None', 'extension': 'txt'},
michael@0 228 {'value': 'bash', 'name': 'Bash', 'extension': 'sh'},
michael@0 229 {'value': 'c', 'name': 'C', 'extension': 'c'},
michael@0 230 {'value': 'cpp', 'name': 'C++', 'extension': 'cpp'},
michael@0 231 {'value': 'html4strict', 'name': 'HTML', 'extension': 'html'},
michael@0 232 {'value': 'javascript', 'name': 'Javascript', 'extension': 'js'},
michael@0 233 {'value': 'javascript', 'name': 'Javascript', 'extension': 'jsm'},
michael@0 234 {'value': 'lua', 'name': 'Lua', 'extension': 'lua'},
michael@0 235 {'value': 'perl', 'name': 'Perl', 'extension': 'pl'},
michael@0 236 {'value': 'php', 'name': 'PHP', 'extension': 'php'},
michael@0 237 {'value': 'python', 'name': 'Python', 'extension': 'py'},
michael@0 238 {'value': 'ruby', 'name': 'Ruby', 'extension': 'rb'},
michael@0 239 {'value': 'css', 'name': 'CSS', 'extension': 'css'},
michael@0 240 {'value': 'diff', 'name': 'Diff', 'extension': 'diff'},
michael@0 241 {'value': 'ini', 'name': 'INI file', 'extension': 'ini'},
michael@0 242 {'value': 'java', 'name': 'Java', 'extension': 'java'},
michael@0 243 {'value': 'xml', 'name': 'XML', 'extension': 'xml'},
michael@0 244 {'value': 'xml', 'name': 'XML', 'extension': 'xul'}]
michael@0 245
michael@0 246 lang = ''
michael@0 247
michael@0 248 if file:
michael@0 249 try:
michael@0 250 with open(file, 'r') as f:
michael@0 251 content = f.read()
michael@0 252 # TODO: Use mime-types instead of extensions; suprocess('file <f_name>')
michael@0 253 # Guess File-type based on file extension
michael@0 254 extension = file.split('.')[-1]
michael@0 255 for l in FILE_TYPES:
michael@0 256 if extension == l['extension']:
michael@0 257 print('Identified file as %s' % l['name'])
michael@0 258 lang = l['value']
michael@0 259 except IOError:
michael@0 260 print('ERROR. No such file')
michael@0 261 return 1
michael@0 262 else:
michael@0 263 content = sys.stdin.read()
michael@0 264 duration = duration[0]
michael@0 265
michael@0 266 if language:
michael@0 267 lang = language
michael@0 268
michael@0 269
michael@0 270 params = [
michael@0 271 ('parent_pid', ''),
michael@0 272 ('format', lang),
michael@0 273 ('code2', content),
michael@0 274 ('poster', poster),
michael@0 275 ('expiry', duration),
michael@0 276 ('paste', 'Send')]
michael@0 277
michael@0 278 data = urllib.urlencode(params)
michael@0 279 print('Uploading ...')
michael@0 280 try:
michael@0 281 req = urllib2.Request(URL, data)
michael@0 282 response = urllib2.urlopen(req)
michael@0 283 http_response_code = response.getcode()
michael@0 284 if http_response_code == 200:
michael@0 285 print(response.geturl())
michael@0 286 else:
michael@0 287 print('Could not upload the file, '
michael@0 288 'HTTP Response Code %s' %(http_response_code))
michael@0 289 except urllib2.URLError:
michael@0 290 print('ERROR. Could not connect to pastebin.mozilla.org.')
michael@0 291 return 1
michael@0 292 return 0
michael@0 293
michael@0 294
michael@0 295 @CommandProvider
michael@0 296 class ReviewboardToolsProvider(MachCommandBase):
michael@0 297 @Command('rbt', category='devenv', allow_all_args=True,
michael@0 298 description='Run Reviewboard Tools')
michael@0 299 @CommandArgument('args', nargs='...', help='Arguments to rbt tool')
michael@0 300 def rbt(self, args):
michael@0 301 if not args:
michael@0 302 args = ['help']
michael@0 303
michael@0 304 self._activate_virtualenv()
michael@0 305 self.virtualenv_manager.install_pip_package('RBTools==0.6')
michael@0 306
michael@0 307 from rbtools.commands.main import main
michael@0 308
michael@0 309 # main() doesn't accept arguments and instead reads from sys.argv. So,
michael@0 310 # we fake it out.
michael@0 311 sys.argv = ['rbt'] + args
michael@0 312 return main()
michael@0 313
michael@0 314 @CommandProvider
michael@0 315 class FormatProvider(MachCommandBase):
michael@0 316 @Command('clang-format', category='misc',
michael@0 317 description='Run clang-format on current changes')
michael@0 318 @CommandArgument('--show', '-s', action = 'store_true',
michael@0 319 help = 'Show diff output on instead of applying changes')
michael@0 320 def clang_format(self, show=False):
michael@0 321 plat = platform.system()
michael@0 322 fmt = plat.lower() + "/clang-format-3.5"
michael@0 323 fmt_diff = "clang-format-diff-3.5"
michael@0 324
michael@0 325 # We are currently using a modified verion of clang-format hosted on people.mozilla.org.
michael@0 326 # This is a temporary work around until we upstream the necessary changes and we can use
michael@0 327 # a system version of clang-format. See bug 961541.
michael@0 328 if plat == "Windows":
michael@0 329 fmt += ".exe"
michael@0 330 else:
michael@0 331 arch = os.uname()[4]
michael@0 332 if (plat != "Linux" and plat != "Darwin") or arch != 'x86_64':
michael@0 333 print("Unsupported platform " + plat + "/" + arch +
michael@0 334 ". Supported platforms are Windows/*, Linux/x86_64 and Darwin/x86_64")
michael@0 335 return 1
michael@0 336
michael@0 337 os.chdir(self.topsrcdir)
michael@0 338 self.prompt = True
michael@0 339
michael@0 340 try:
michael@0 341 if not self.locate_or_fetch(fmt):
michael@0 342 return 1
michael@0 343 clang_format_diff = self.locate_or_fetch(fmt_diff)
michael@0 344 if not clang_format_diff:
michael@0 345 return 1
michael@0 346
michael@0 347 except urllib2.HTTPError as e:
michael@0 348 print("HTTP error {0}: {1}".format(e.code, e.reason))
michael@0 349 return 1
michael@0 350
michael@0 351 from subprocess import Popen, PIPE
michael@0 352
michael@0 353 if os.path.exists(".hg"):
michael@0 354 diff_process = Popen(["hg", "diff", "-U0", "-r", "tip^",
michael@0 355 "--include", "glob:**.c", "--include", "glob:**.cpp", "--include", "glob:**.h",
michael@0 356 "--exclude", "listfile:.clang-format-ignore"], stdout=PIPE)
michael@0 357 else:
michael@0 358 git_process = Popen(["git", "diff", "-U0", "HEAD^"], stdout=PIPE)
michael@0 359 try:
michael@0 360 diff_process = Popen(["filterdiff", "--include=*.h", "--include=*.cpp",
michael@0 361 "--exclude-from-file=.clang-format-ignore"],
michael@0 362 stdin=git_process.stdout, stdout=PIPE)
michael@0 363 except OSError as e:
michael@0 364 if e.errno == errno.ENOENT:
michael@0 365 print("Can't find filterdiff. Please install patchutils.")
michael@0 366 else:
michael@0 367 print("OSError {0}: {1}".format(e.code, e.reason))
michael@0 368 return 1
michael@0 369
michael@0 370
michael@0 371 args = [sys.executable, clang_format_diff, "-p1"]
michael@0 372 if not show:
michael@0 373 args.append("-i")
michael@0 374 cf_process = Popen(args, stdin=diff_process.stdout)
michael@0 375 return cf_process.communicate()[0]
michael@0 376
michael@0 377 def locate_or_fetch(self, root):
michael@0 378 target = os.path.join(self._mach_context.state_dir, os.path.basename(root))
michael@0 379 if not os.path.exists(target):
michael@0 380 site = "https://people.mozilla.org/~ajones/clang-format/"
michael@0 381 if self.prompt and raw_input("Download clang-format executables from {0} (yN)? ".format(site)).lower() != 'y':
michael@0 382 print("Download aborted.")
michael@0 383 return 1
michael@0 384 self.prompt = False
michael@0 385
michael@0 386 u = site + root
michael@0 387 print("Downloading {0} to {1}".format(u, target))
michael@0 388 data = urllib2.urlopen(url=u).read()
michael@0 389 temp = target + ".tmp"
michael@0 390 with open(temp, "wb") as fh:
michael@0 391 fh.write(data)
michael@0 392 fh.close()
michael@0 393 os.chmod(temp, os.stat(temp).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
michael@0 394 os.rename(temp, target)
michael@0 395 return target
michael@0 396

mercurial