diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 5278b97571fc..290773561f17 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -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. diff --git a/python/mozbuild/mozbuild/frontend/mach_commands.py b/python/mozbuild/mozbuild/frontend/mach_commands.py index 4936ae322b98..b8442cf6b0a0 100644 --- a/python/mozbuild/mozbuild/frontend/mach_commands.py +++ b/python/mozbuild/mozbuild/frontend/mach_commands.py @@ -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)