--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -713,21 +713,28 @@ class ArtifactCache(CacheManager):
self.log(logging.INFO, 'artifact',
{'filename': result + PROCESSED_SUFFIX},
'Last installed binaries from local processed file {filename}')
class Artifacts(object):
'''Maintain state to efficiently fetch build artifacts from a Firefox tree.'''
- def __init__(self, tree, job=None, log=None, cache_dir='.', hg='hg', skip_cache=False):
+ def __init__(self, tree, job=None, log=None, cache_dir='.', hg=None, git=None, skip_cache=False):
+ if (hg and git) or (not hg and not git):
+ raise ValueError("Must provide path to exactly one of hg and git")
+
self._tree = tree
self._job = job or self._guess_artifact_job()
self._log = log
self._hg = hg
+ self._git = git
+ if self._git:
+ import which
+ self._cinnabar = which.which('git-cinnabar')
self._cache_dir = cache_dir
self._skip_cache = skip_cache
try:
self._artifact_job = get_job_details(self._job, log=self._log)
except KeyError:
self.log(logging.INFO, 'artifact',
{'job': self._job},
@@ -800,20 +807,47 @@ class Artifacts(object):
{'tree': tree,
'pushid': pushid,
'num': NUM_PUSHHEADS_TO_QUERY_PER_PARENT},
'Retrieving the last {num} pushheads starting with id {pushid} on {tree}')
candidate_pushheads.extend(pushhead_cache.pushid_range(tree, start, end))
return candidate_pushheads
+ def _get_hg_revisions_from_git(self):
+
+ # First commit is HEAD, next is HEAD~1, etc.
+ rev_list = subprocess.check_output([
+ self._git, 'rev-list', '--topo-order',
+ 'HEAD~{num}..HEAD'.format(num=NUM_REVISIONS_TO_QUERY),
+ ])
+
+ hg_hash_list = subprocess.check_output([
+ self._cinnabar, 'git2hg'
+ ] + rev_list.splitlines())
+
+ zeroes = "0" * 40
+
+ hashes = []
+ for hg_hash in hg_hash_list.splitlines():
+ hg_hash = hg_hash.strip()
+ if not hg_hash or hg_hash == zeroes:
+ continue
+ hashes.append(hg_hash)
+ return hashes
+
def _get_recent_public_revisions(self):
"""Returns recent ancestors of the working parent that are likely to
to be known to mozilla automation.
+
+ If we're using git, retrieves hg revisions from git-cinnabar.
"""
+ if self._git:
+ return self._get_hg_revisions_from_git()
+
return subprocess.check_output([
self._hg, 'log',
'--template', '{node}\n',
'-r', 'last(public() and ::., {num})'.format(
num=NUM_REVISIONS_TO_QUERY)
]).splitlines()
def _find_pushheads(self):
@@ -940,25 +974,32 @@ class Artifacts(object):
return 1
return 0
self.log(logging.ERROR, 'artifact',
{'count': count},
'Tried {count} pushheads, no built artifacts found.')
return 1
- def install_from_hg_recent(self, distdir):
+ def install_from_recent(self, distdir):
hg_pushheads = self._find_pushheads()
return self._install_from_hg_pushheads(hg_pushheads, distdir)
- def install_from_hg_revset(self, revset, distdir):
- revision = subprocess.check_output([self._hg, 'log', '--template', '{node}\n',
- '-r', revset]).strip()
- if len(revision.split('\n')) != 1:
- raise ValueError('hg revision specification must resolve to exactly one commit')
+ def install_from_revset(self, revset, distdir):
+ if self._hg:
+ revision = subprocess.check_output([self._hg, 'log', '--template', '{node}\n',
+ '-r', revset]).strip()
+ if len(revision.split('\n')) != 1:
+ raise ValueError('hg revision specification must resolve to exactly one commit')
+ else:
+ revision = subprocess.check_output([self._git, 'rev-parse', revset]).strip()
+ revision = subprocess.check_output([self._cinnabar, 'git2hg', revision]).strip()
+ if len(revision.split('\n')) != 1:
+ raise ValueError('hg revision specification must resolve to exactly one commit')
+
self.log(logging.INFO, 'artifact',
{'revset': revset,
'revision': revision},
'Will only accept artifacts from a pushhead at {revision} '
'(matched revset "{revset}").')
return self._install_from_hg_pushheads([revision], distdir)
def install_from(self, source, distdir):
@@ -968,19 +1009,19 @@ class Artifacts(object):
return self.install_from_file(source, distdir)
elif source and urlparse.urlparse(source).scheme:
return self.install_from_url(source, distdir)
else:
if source is None and 'MOZ_ARTIFACT_REVISION' in os.environ:
source = os.environ['MOZ_ARTIFACT_REVISION']
if source:
- return self.install_from_hg_revset(source, distdir)
+ return self.install_from_revset(source, distdir)
- return self.install_from_hg_recent(distdir)
+ return self.install_from_recent(distdir)
def print_last(self):
self.log(logging.INFO, 'artifact',
{},
'Printing last used artifact details.')
self._tree_cache.print_last()
self._task_cache.print_last()
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1454,20 +1454,17 @@ class ArtifactSubCommand(SubCommand):
return after
@CommandProvider
class PackageFrontend(MachCommandBase):
"""Fetch and install binary artifacts from Mozilla automation."""
@Command('artifact', category='post-build',
- description='Use pre-built artifacts to build Firefox.',
- conditions=[
- conditions.is_hg, # mercurial only for now.
- ])
+ description='Use pre-built artifacts to build Firefox.')
def artifact(self):
'''Download, cache, and install pre-built binary artifacts to build Firefox.
Use |mach build| as normal to freshen your installed binary libraries:
artifact builds automatically download, cache, and install binary
artifacts from Mozilla automation, replacing whatever may be in your
object directory. Use |mach artifact last| to see what binary artifacts
were last used.
@@ -1491,24 +1488,37 @@ class PackageFrontend(MachCommandBase):
try:
os.makedirs(cache_dir)
except OSError as e:
if e.errno != errno.EEXIST:
raise
import which
- if self._is_windows():
- hg = which.which('hg.exe')
- else:
- hg = which.which('hg')
+
+ here = os.path.abspath(os.path.dirname(__file__))
+ build_obj = MozbuildObject.from_environment(cwd=here)
+
+ hg = None
+ if conditions.is_hg(build_obj):
+ if self._is_windows():
+ hg = which.which('hg.exe')
+ else:
+ hg = which.which('hg')
+
+ git = None
+ if conditions.is_git(build_obj):
+ if self._is_windows():
+ git = which.which('git.exe')
+ else:
+ git = which.which('git')
# Absolutely must come after the virtualenv is populated!
from mozbuild.artifacts import Artifacts
- artifacts = Artifacts(tree, job, log=self.log, cache_dir=cache_dir, hg=hg, skip_cache=skip_cache)
+ artifacts = Artifacts(tree, job, log=self.log, cache_dir=cache_dir, skip_cache=skip_cache, hg=hg, git=git)
return artifacts
@ArtifactSubCommand('artifact', 'install',
'Install a good pre-built artifact.')
@CommandArgument('source', metavar='SRC', nargs='?', type=str,
help='Where to fetch and install artifacts from. Can be omitted, in '
'which case the current hg repository is inspected; an hg revision; '
'a remote URL; or a local file.',