1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
5 from __future__
import unicode_literals
14 from mach
.decorators
import (
20 from mozbuild
.base
import MachCommandBase
24 class SearchProvider(object):
25 @Command('mxr', category
='misc',
26 description
='Search for something in MXR.')
27 @CommandArgument('term', nargs
='+', help='Term(s) to search for.')
31 uri
= 'https://mxr.mozilla.org/mozilla-central/search?string=%s' % term
32 webbrowser
.open_new_tab(uri
)
34 @Command('dxr', category
='misc',
35 description
='Search for something in DXR.')
36 @CommandArgument('term', nargs
='+', help='Term(s) to search for.')
40 uri
= 'http://dxr.mozilla.org/mozilla-central/search?q=%s&redirect=true' % term
41 webbrowser
.open_new_tab(uri
)
43 @Command('mdn', category
='misc',
44 description
='Search for something on MDN.')
45 @CommandArgument('term', nargs
='+', help='Term(s) to search for.')
49 uri
= 'https://developer.mozilla.org/search?q=%s' % term
50 webbrowser
.open_new_tab(uri
)
52 @Command('google', category
='misc',
53 description
='Search for something on Google.')
54 @CommandArgument('term', nargs
='+', help='Term(s) to search for.')
55 def google(self
, term
):
58 uri
= 'https://www.google.com/search?q=%s' % term
59 webbrowser
.open_new_tab(uri
)
61 @Command('search', category
='misc',
62 description
='Search for something on the Internets. '
63 'This will open 3 new browser tabs and search for the term on Google, '
65 @CommandArgument('term', nargs
='+', help='Term(s) to search for.')
66 def search(self
, term
):
72 class Interface(object):
74 Represents an XPIDL interface, in what file it is defined, what it derives
75 from, what its uuid is, and where in the source file the uuid is.
77 def __init__(self
, filename
, production
):
79 assert isinstance(production
, xpidl
.Interface
)
80 self
.name
= production
.name
81 self
.base
= production
.base
82 self
.filename
= filename
83 self
.uuid
= production
.attributes
.uuid
84 location
= production
.location
85 data
= location
._lexdata
86 attr_pos
= data
.rfind(b
'[', 0, location
._lexpos
)
87 # uuid is always lowercase, but actual file content may not be.
88 self
.uuid_pos
= data
[attr_pos
:location
._lexpos
].lower() \
89 .rfind(self
.uuid
) + attr_pos
92 class InterfaceRegistry(object):
94 Tracks XPIDL interfaces, and allow to search them by name and by the
95 interface they derive from.
101 def get_by_name(self
, name
):
102 return self
.by_name
.get(name
, [])
104 def get_by_base(self
, base
):
105 return self
.by_base
.get(base
, [])
107 def add(self
, interface
):
108 l
= self
.by_name
.setdefault(interface
.name
, [])
110 l
= self
.by_base
.setdefault(interface
.base
, [])
114 class IDLUpdater(object):
116 Updates interfaces uuids in IDL files.
118 def __init__(self
, interfaces
):
119 from mozpack
.copier
import FileRegistry
120 self
.interfaces
= interfaces
;
121 self
.registry
= FileRegistry()
124 for interface
in self
.interfaces
.get_by_name(name
):
127 def _add(self
, interface
):
128 from mozpack
.files
import GeneratedFile
129 from uuid
import uuid4
130 path
= interface
.filename
131 if not self
.registry
.contains(path
):
132 self
.registry
.add(path
, GeneratedFile(open(path
).read()))
133 content
= self
.registry
[path
].content
134 content
= content
[:interface
.uuid_pos
] + str(uuid4()) + \
135 content
[interface
.uuid_pos
+ len(interface
.uuid
):]
136 self
.registry
[path
].content
= content
138 # Recurse through all the interfaces deriving from this one
139 for derived
in self
.interfaces
.get_by_base(interface
.name
):
143 for p
, f
in self
.registry
:
148 class UUIDProvider(object):
149 @Command('uuid', category
='misc',
150 description
='Generate a uuid.')
151 @CommandArgument('--format', '-f', choices
=['idl', 'cpp', 'c++'],
152 help='Output format for the generated uuid.')
153 def uuid(self
, format
=None):
156 if format
in [None, 'idl']:
160 if format
in [None, 'cpp', 'c++']:
162 print('{ 0x%s, 0x%s, 0x%s, \\' % (u
[0:8], u
[8:12], u
[12:16]))
163 pairs
= tuple(map(lambda n
: u
[n
:n
+2], range(16, 32, 2)))
164 print((' { ' + '0x%s, ' * 7 + '0x%s } }') % pairs
)
166 @Command('update-uuids', category
='misc',
167 description
='Update IDL files with new UUIDs.')
168 @CommandArgument('--path', default
='.',
169 help='Base path under which uuids will be searched.')
170 @CommandArgument('interfaces', nargs
='+',
171 help='Changed interfaces whose UUIDs need to be updated. ' +
172 'Their descendants are updated as well.')
173 def update_uuids(self
, path
, interfaces
):
176 from mozpack
.files
import FileFinder
178 from tempfile
import mkdtemp
180 finder
= FileFinder(path
, find_executables
=False)
181 # Avoid creating xpidllex and xpidlyacc in the current directory.
184 parser
= xpidl
.IDLParser(outputdir
=tmpdir
)
185 registry
= InterfaceRegistry()
186 for p
, f
in finder
.find('**/*.idl'):
187 p
= mozpack
.path
.join(path
, p
)
189 content
= f
.open().read()
190 idl
= parser
.parse(content
, filename
=p
)
193 for prod
in idl
.productions
:
194 if isinstance(prod
, xpidl
.Interface
):
195 registry
.add(Interface(p
, prod
))
198 shutil
.rmtree(tmpdir
)
200 updates
= IDLUpdater(registry
)
202 for interface
in interfaces
:
203 updates
.add(interface
)
208 class PastebinProvider(object):
209 @Command('pastebin', category
='misc',
210 description
='Command line interface to pastebin.mozilla.org.')
211 @CommandArgument('--language', default
=None,
212 help='Language to use for syntax highlighting')
213 @CommandArgument('--poster', default
=None,
214 help='Specify your name for use with pastebin.mozilla.org')
215 @CommandArgument('--duration', default
='day',
216 choices
=['d', 'day', 'm', 'month', 'f', 'forever'],
217 help='Keep for specified duration (default: %(default)s)')
218 @CommandArgument('file', nargs
='?', default
=None,
219 help='Specify the file to upload to pastebin.mozilla.org')
221 def pastebin(self
, language
, poster
, duration
, file):
225 URL
= 'http://pastebin.mozilla.org/'
227 FILE_TYPES
= [{'value': 'text', 'name': 'None', 'extension': 'txt'},
228 {'value': 'bash', 'name': 'Bash', 'extension': 'sh'},
229 {'value': 'c', 'name': 'C', 'extension': 'c'},
230 {'value': 'cpp', 'name': 'C++', 'extension': 'cpp'},
231 {'value': 'html4strict', 'name': 'HTML', 'extension': 'html'},
232 {'value': 'javascript', 'name': 'Javascript', 'extension': 'js'},
233 {'value': 'javascript', 'name': 'Javascript', 'extension': 'jsm'},
234 {'value': 'lua', 'name': 'Lua', 'extension': 'lua'},
235 {'value': 'perl', 'name': 'Perl', 'extension': 'pl'},
236 {'value': 'php', 'name': 'PHP', 'extension': 'php'},
237 {'value': 'python', 'name': 'Python', 'extension': 'py'},
238 {'value': 'ruby', 'name': 'Ruby', 'extension': 'rb'},
239 {'value': 'css', 'name': 'CSS', 'extension': 'css'},
240 {'value': 'diff', 'name': 'Diff', 'extension': 'diff'},
241 {'value': 'ini', 'name': 'INI file', 'extension': 'ini'},
242 {'value': 'java', 'name': 'Java', 'extension': 'java'},
243 {'value': 'xml', 'name': 'XML', 'extension': 'xml'},
244 {'value': 'xml', 'name': 'XML', 'extension': 'xul'}]
250 with
open(file, 'r') as f
:
252 # TODO: Use mime-types instead of extensions; suprocess('file <f_name>')
253 # Guess File-type based on file extension
254 extension
= file.split('.')[-1]
256 if extension
== l
['extension']:
257 print('Identified file as %s' % l
['name'])
260 print('ERROR. No such file')
263 content
= sys
.stdin
.read()
264 duration
= duration
[0]
275 ('expiry', duration
),
278 data
= urllib
.urlencode(params
)
279 print('Uploading ...')
281 req
= urllib2
.Request(URL
, data
)
282 response
= urllib2
.urlopen(req
)
283 http_response_code
= response
.getcode()
284 if http_response_code
== 200:
285 print(response
.geturl())
287 print('Could not upload the file, '
288 'HTTP Response Code %s' %(http_response_code))
289 except urllib2
.URLError
:
290 print('ERROR. Could not connect to pastebin.mozilla.org.')
296 class ReviewboardToolsProvider(MachCommandBase
):
297 @Command('rbt', category
='devenv', allow_all_args
=True,
298 description
='Run Reviewboard Tools')
299 @CommandArgument('args', nargs
='...', help='Arguments to rbt tool')
304 self
._activate
_virtualenv
()
305 self
.virtualenv_manager
.install_pip_package('RBTools==0.6')
307 from rbtools
.commands
.main
import main
309 # main() doesn't accept arguments and instead reads from sys.argv. So,
311 sys
.argv
= ['rbt'] + args
315 class FormatProvider(MachCommandBase
):
316 @Command('clang-format', category
='misc',
317 description
='Run clang-format on current changes')
318 @CommandArgument('--show', '-s', action
= 'store_true',
319 help = 'Show diff output on instead of applying changes')
320 def clang_format(self
, show
=False):
321 plat
= platform
.system()
322 fmt
= plat
.lower() + "/clang-format-3.5"
323 fmt_diff
= "clang-format-diff-3.5"
325 # We are currently using a modified version of clang-format hosted on people.mozilla.org.
326 # This is a temporary work around until we upstream the necessary changes and we can use
327 # a system version of clang-format. See bug 961541.
328 if plat
== "Windows":
332 if (plat
!= "Linux" and plat
!= "Darwin") or arch
!= 'x86_64':
333 print("Unsupported platform " + plat
+ "/" + arch
+
334 ". Supported platforms are Windows/*, Linux/x86_64 and Darwin/x86_64")
337 os
.chdir(self
.topsrcdir
)
341 if not self
.locate_or_fetch(fmt
):
343 clang_format_diff
= self
.locate_or_fetch(fmt_diff
)
344 if not clang_format_diff
:
347 except urllib2
.HTTPError
as e
:
348 print("HTTP error {0}: {1}".format(e
.code
, e
.reason
))
351 from subprocess
import Popen
, PIPE
353 if os
.path
.exists(".hg"):
354 diff_process
= Popen(["hg", "diff", "-U0", "-r", "tip^",
355 "--include", "glob:**.c", "--include", "glob:**.cpp", "--include", "glob:**.h",
356 "--exclude", "listfile:.clang-format-ignore"], stdout
=PIPE
)
358 git_process
= Popen(["git", "diff", "-U0", "HEAD^"], stdout
=PIPE
)
360 diff_process
= Popen(["filterdiff", "--include=*.h", "--include=*.cpp",
361 "--exclude-from-file=.clang-format-ignore"],
362 stdin
=git_process
.stdout
, stdout
=PIPE
)
364 if e
.errno
== errno
.ENOENT
:
365 print("Can't find filterdiff. Please install patchutils.")
367 print("OSError {0}: {1}".format(e
.code
, e
.reason
))
371 args
= [sys
.executable
, clang_format_diff
, "-p1"]
374 cf_process
= Popen(args
, stdin
=diff_process
.stdout
)
375 return cf_process
.communicate()[0]
377 def locate_or_fetch(self
, root
):
378 target
= os
.path
.join(self
._mach
_context
.state_dir
, os
.path
.basename(root
))
379 if not os
.path
.exists(target
):
380 site
= "https://people.mozilla.org/~ajones/clang-format/"
381 if self
.prompt
and raw_input("Download clang-format executables from {0} (yN)? ".format(site
)).lower() != 'y':
382 print("Download aborted.")
387 print("Downloading {0} to {1}".format(u
, target
))
388 data
= urllib2
.urlopen(url
=u
).read()
389 temp
= target
+ ".tmp"
390 with
open(temp
, "wb") as fh
:
393 os
.chmod(temp
, os
.stat(temp
).st_mode | stat
.S_IXUSR | stat
.S_IXGRP | stat
.S_IXOTH
)
394 os
.rename(temp
, target
)