blob: cbecc11375a6a048d7f6bcbd59e527889fa50391 [file] [log] [blame]
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from __future__ import absolute_import, print_function, unicode_literals
from collections import defaultdict
import os
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
SubCommand,
)
from mozbuild.base import MachCommandBase
import mozpack.path as mozpath
class InvalidPathException(Exception):
"""Represents an error due to an invalid path."""
@CommandProvider
class MozbuildFileCommands(MachCommandBase):
@Command('mozbuild-reference', category='build-dev',
description='View reference documentation on mozbuild files.')
@CommandArgument('symbol', default=None, nargs='*',
help='Symbol to view help on. If not specified, all will be shown.')
@CommandArgument('--name-only', '-n', default=False, action='store_true',
help='Print symbol names only.')
def reference(self, symbol, name_only=False):
# mozbuild.sphinx imports some Sphinx modules, so we need to be sure
# the optional Sphinx package is installed.
self._activate_virtualenv()
self.virtualenv_manager.install_pip_package('Sphinx==1.1.3')
from mozbuild.sphinx import (
format_module,
function_reference,
special_reference,
variable_reference,
)
import mozbuild.frontend.context as m
if name_only:
for s in sorted(m.VARIABLES.keys()):
print(s)
for s in sorted(m.FUNCTIONS.keys()):
print(s)
for s in sorted(m.SPECIAL_VARIABLES.keys()):
print(s)
return 0
if len(symbol):
for s in symbol:
if s in m.VARIABLES:
for line in variable_reference(s, *m.VARIABLES[s]):
print(line)
continue
elif s in m.FUNCTIONS:
for line in function_reference(s, *m.FUNCTIONS[s]):
print(line)
continue
elif s in m.SPECIAL_VARIABLES:
for line in special_reference(s, *m.SPECIAL_VARIABLES[s]):
print(line)
continue
print('Could not find symbol: %s' % s)
return 1
return 0
for line in format_module(m):
print(line)
return 0
@Command('file-info', category='build-dev',
description='Query for metadata about files.')
def file_info(self):
"""Show files metadata derived from moz.build files.
moz.build files contain "Files" sub-contexts for declaring metadata
against file patterns. This command suite is used to query that data.
"""
@SubCommand('file-info', 'bugzilla-component',
'Show Bugzilla component info for files listed.')
@CommandArgument('-r', '--rev',
help='Version control revision to look up info from')
@CommandArgument('paths', nargs='+',
help='Paths whose data to query')
def file_info_bugzilla(self, paths, rev=None):
"""Show Bugzilla component for a set of files.
Given a requested set of files (which can be specified using
wildcards), print the Bugzilla component for each file.
"""
components = defaultdict(set)
try:
for p, m in self._get_files_info(paths, rev=rev).items():
components[m.get('BUG_COMPONENT')].add(p)
except InvalidPathException as e:
print(e.message)
return 1
for component, files in sorted(components.items(), key=lambda x: (x is None, x)):
print('%s :: %s' % (component.product, component.component) if component else 'UNKNOWN')
for f in sorted(files):
print(' %s' % f)
@SubCommand('file-info', 'missing-bugzilla',
'Show files missing Bugzilla component info')
@CommandArgument('-r', '--rev',
help='Version control revision to look up info from')
@CommandArgument('paths', nargs='+',
help='Paths whose data to query')
def file_info_missing_bugzilla(self, paths, rev=None):
try:
for p, m in sorted(self._get_files_info(paths, rev=rev).items()):
if 'BUG_COMPONENT' not in m:
print(p)
except InvalidPathException as e:
print(e.message)
return 1
@SubCommand('file-info', 'dep-tests',
'Show test files marked as dependencies of these source files.')
@CommandArgument('-r', '--rev',
help='Version control revision to look up info from')
@CommandArgument('paths', nargs='+',
help='Paths whose data to query')
def file_info_test_deps(self, paths, rev=None):
try:
for p, m in self._get_files_info(paths, rev=rev).items():
print('%s:' % mozpath.relpath(p, self.topsrcdir))
if m.test_files:
print('\tTest file patterns:')
for p in m.test_files:
print('\t\t%s' % p)
if m.test_tags:
print('\tRelevant tags:')
for p in m.test_tags:
print('\t\t%s' % p)
if m.test_flavors:
print('\tRelevant flavors:')
for p in m.test_flavors:
print('\t\t%s' % p)
except InvalidPathException as e:
print(e.message)
return 1
def _get_reader(self, finder):
from mozbuild.frontend.reader import (
BuildReader,
EmptyConfig,
)
config = EmptyConfig(self.topsrcdir)
return BuildReader(config, finder=finder)
def _get_files_info(self, paths, rev=None):
from mozbuild.frontend.reader import default_finder
from mozpack.files import FileFinder, MercurialRevisionFinder
# Normalize to relative from topsrcdir.
relpaths = []
for p in paths:
a = mozpath.abspath(p)
if not mozpath.basedir(a, [self.topsrcdir]):
raise InvalidPathException('path is outside topsrcdir: %s' % p)
relpaths.append(mozpath.relpath(a, self.topsrcdir))
repo = None
if rev:
hg_path = os.path.join(self.topsrcdir, '.hg')
if not os.path.exists(hg_path):
raise InvalidPathException('a Mercurial repo is required '
'when specifying a revision')
repo = self.topsrcdir
# We need two finders because the reader's finder operates on
# absolute paths.
finder = FileFinder(self.topsrcdir, find_executables=False)
if repo:
reader_finder = MercurialRevisionFinder(repo, rev=rev,
recognize_repo_paths=True)
else:
reader_finder = default_finder
# Expand wildcards.
allpaths = []
for p in relpaths:
if '*' not in p:
if p not in allpaths:
allpaths.append(p)
continue
if repo:
raise InvalidPathException('cannot use wildcard in version control mode')
for path, f in finder.find(p):
if path not in allpaths:
allpaths.append(path)
reader = self._get_reader(finder=reader_finder)
return reader.files_info(allpaths)