Bug 1397406 - Add a helper function to retrieve a BuildReader; r=dustin

The code for obtaining a BuildReader for evaluating moz.build files
is generic and non-trivial. We already had a custom implementation
for `mach file-info` that implemented support for Mercurial
integration. Bug 1383880 will introduce a second consumer.

So this commit factors out the "obtain a BuildReader" logic into
a reusable function on our base MozbuildObject class. This makes
it easily available to various parts of the build system and mach
commands.

As part of the change, we detect when ``.`` is being used as the
revision and verify the working directory is clean. This behavior
can be disabled via argument if unwanted. But it's useful by default
to ensure consumers aren't expecting to read uncommitted changes.

MozReview-Commit-ID: LeYFqAb3HAe

--HG--
extra : rebase_source : d5ed4e4f5570a58a68853188de2225cd4e64ab3a
This commit is contained in:
Gregory Szorc 2017-09-06 12:18:51 -07:00
parent 2cf6305161
commit 069ee9c5d6
2 changed files with 78 additions and 32 deletions

View File

@ -17,6 +17,7 @@ from mach.mixin.process import ProcessExecutionMixin
from mozversioncontrol import (
get_repository_from_build_config,
get_repository_object,
InvalidRepoPath,
)
from .backend.configenvironment import ConfigEnvironment
@ -298,6 +299,80 @@ class MozbuildObject(ProcessExecutionMixin):
return get_repository_object(self.topsrcdir)
def mozbuild_reader(self, config_mode='build', vcs_revision=None,
vcs_check_clean=True):
"""Obtain a ``BuildReader`` for evaluating moz.build files.
Given arguments, returns a ``mozbuild.frontend.reader.BuildReader``
that can be used to evaluate moz.build files for this repo.
``config_mode`` is either ``build`` or ``empty``. If ``build``,
``self.config_environment`` is used. This requires a configured build
system to work. If ``empty``, an empty config is used. ``empty`` is
appropriate for file-based traversal mode where ``Files`` metadata is
read.
If ``vcs_revision`` is defined, it specifies a version control revision
to use to obtain files content. The default is to use the filesystem.
This mode is only supported with Mercurial repositories.
If ``vcs_revision`` is not defined and the version control checkout is
sparse, this implies ``vcs_revision='.'``.
If ``vcs_revision`` is ``.`` (denotes the parent of the working
directory), we will verify that the working directory is clean unless
``vcs_check_clean`` is False. This prevents confusion due to uncommitted
file changes not being reflected in the reader.
"""
from mozbuild.frontend.reader import (
default_finder,
BuildReader,
EmptyConfig,
)
from mozpack.files import (
MercurialRevisionFinder,
)
if config_mode == 'build':
config = self.config_environment
elif config_mode == 'empty':
config = EmptyConfig(self.topsrcdir)
else:
raise ValueError('unknown config_mode value: %s' % config_mode)
try:
repo = self.repository
except InvalidRepoPath:
repo = None
if repo and not vcs_revision and repo.sparse_checkout_present():
vcs_revision = '.'
if vcs_revision is None:
finder = default_finder
else:
# If we failed to detect the repo prior, check again to raise its
# exception.
if not repo:
self.repository
assert False
if repo.name != 'hg':
raise Exception('do not support VCS reading mode for %s' %
repo.name)
if vcs_revision == '.' and vcs_check_clean:
with repo:
if not repo.working_directory_clean():
raise Exception('working directory is not clean; '
'refusing to use a VCS-based finder')
finder = MercurialRevisionFinder(self.topsrcdir, rev=vcs_revision,
recognize_repo_paths=True)
return BuildReader(config, finder=finder)
@memoized_property
def python3(self):
"""Obtain info about a Python 3 executable.

View File

@ -159,18 +159,8 @@ class MozbuildFileCommands(MachCommandBase):
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
reader = self.mozbuild_reader(config_mode='empty', vcs_revision=rev)
# Normalize to relative from topsrcdir.
relpaths = []
@ -181,24 +171,6 @@ class MozbuildFileCommands(MachCommandBase):
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)
if repo:
reader_finder = MercurialRevisionFinder(repo, rev=rev,
recognize_repo_paths=True)
else:
reader_finder = default_finder
# Expand wildcards.
# One variable is for ordering. The other for membership tests.
# (Membership testing on a list can be slow.)
@ -211,13 +183,12 @@ class MozbuildFileCommands(MachCommandBase):
allpaths.append(p)
continue
if repo:
if rev:
raise InvalidPathException('cannot use wildcard in version control mode')
for path, f in finder.find(p):
for path, f in reader.finder.find(p):
if path not in all_paths_set:
all_paths_set.add(path)
allpaths.append(path)
reader = self._get_reader(finder=reader_finder)
return reader.files_info(allpaths)