271 lines
10 KiB
Python
271 lines
10 KiB
Python
|
# ex:ts=4:sw=4:sts=4:et
|
||
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||
|
"""
|
||
|
BitBake 'Fetch' implementation for mercurial DRCS (hg).
|
||
|
|
||
|
"""
|
||
|
|
||
|
# Copyright (C) 2003, 2004 Chris Larson
|
||
|
# Copyright (C) 2004 Marcin Juszkiewicz
|
||
|
# Copyright (C) 2007 Robert Schuster
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License version 2 as
|
||
|
# published by the Free Software Foundation.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License along
|
||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
#
|
||
|
# Based on functions from the base bb module, Copyright 2003 Holger Schurig
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
import logging
|
||
|
import bb
|
||
|
import errno
|
||
|
from bb.fetch2 import FetchMethod
|
||
|
from bb.fetch2 import FetchError
|
||
|
from bb.fetch2 import MissingParameterError
|
||
|
from bb.fetch2 import runfetchcmd
|
||
|
from bb.fetch2 import logger
|
||
|
|
||
|
class Hg(FetchMethod):
|
||
|
"""Class to fetch from mercurial repositories"""
|
||
|
def supports(self, ud, d):
|
||
|
"""
|
||
|
Check to see if a given url can be fetched with mercurial.
|
||
|
"""
|
||
|
return ud.type in ['hg']
|
||
|
|
||
|
def supports_checksum(self, urldata):
|
||
|
"""
|
||
|
Don't require checksums for local archives created from
|
||
|
repository checkouts.
|
||
|
"""
|
||
|
return False
|
||
|
|
||
|
def urldata_init(self, ud, d):
|
||
|
"""
|
||
|
init hg specific variable within url data
|
||
|
"""
|
||
|
if not "module" in ud.parm:
|
||
|
raise MissingParameterError('module', ud.url)
|
||
|
|
||
|
ud.module = ud.parm["module"]
|
||
|
|
||
|
if 'protocol' in ud.parm:
|
||
|
ud.proto = ud.parm['protocol']
|
||
|
elif not ud.host:
|
||
|
ud.proto = 'file'
|
||
|
else:
|
||
|
ud.proto = "hg"
|
||
|
|
||
|
ud.setup_revisions(d)
|
||
|
|
||
|
if 'rev' in ud.parm:
|
||
|
ud.revision = ud.parm['rev']
|
||
|
elif not ud.revision:
|
||
|
ud.revision = self.latest_revision(ud, d)
|
||
|
|
||
|
# Create paths to mercurial checkouts
|
||
|
hgsrcname = '%s_%s_%s' % (ud.module.replace('/', '.'), \
|
||
|
ud.host, ud.path.replace('/', '.'))
|
||
|
mirrortarball = 'hg_%s.tar.gz' % hgsrcname
|
||
|
ud.fullmirror = os.path.join(d.getVar("DL_DIR"), mirrortarball)
|
||
|
ud.mirrortarballs = [mirrortarball]
|
||
|
|
||
|
hgdir = d.getVar("HGDIR") or (d.getVar("DL_DIR") + "/hg/")
|
||
|
ud.pkgdir = os.path.join(hgdir, hgsrcname)
|
||
|
ud.moddir = os.path.join(ud.pkgdir, ud.module)
|
||
|
ud.localfile = ud.moddir
|
||
|
ud.basecmd = d.getVar("FETCHCMD_hg") or "/usr/bin/env hg"
|
||
|
|
||
|
ud.write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS")
|
||
|
|
||
|
def need_update(self, ud, d):
|
||
|
revTag = ud.parm.get('rev', 'tip')
|
||
|
if revTag == "tip":
|
||
|
return True
|
||
|
if not os.path.exists(ud.localpath):
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def try_premirror(self, ud, d):
|
||
|
# If we don't do this, updating an existing checkout with only premirrors
|
||
|
# is not possible
|
||
|
if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
|
||
|
return True
|
||
|
if os.path.exists(ud.moddir):
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def _buildhgcommand(self, ud, d, command):
|
||
|
"""
|
||
|
Build up an hg commandline based on ud
|
||
|
command is "fetch", "update", "info"
|
||
|
"""
|
||
|
|
||
|
proto = ud.parm.get('protocol', 'http')
|
||
|
|
||
|
host = ud.host
|
||
|
if proto == "file":
|
||
|
host = "/"
|
||
|
ud.host = "localhost"
|
||
|
|
||
|
if not ud.user:
|
||
|
hgroot = host + ud.path
|
||
|
else:
|
||
|
if ud.pswd:
|
||
|
hgroot = ud.user + ":" + ud.pswd + "@" + host + ud.path
|
||
|
else:
|
||
|
hgroot = ud.user + "@" + host + ud.path
|
||
|
|
||
|
if command == "info":
|
||
|
return "%s identify -i %s://%s/%s" % (ud.basecmd, proto, hgroot, ud.module)
|
||
|
|
||
|
options = [];
|
||
|
|
||
|
# Don't specify revision for the fetch; clone the entire repo.
|
||
|
# This avoids an issue if the specified revision is a tag, because
|
||
|
# the tag actually exists in the specified revision + 1, so it won't
|
||
|
# be available when used in any successive commands.
|
||
|
if ud.revision and command != "fetch":
|
||
|
options.append("-r %s" % ud.revision)
|
||
|
|
||
|
if command == "fetch":
|
||
|
if ud.user and ud.pswd:
|
||
|
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" clone %s %s://%s/%s %s" % (ud.basecmd, ud.user, ud.pswd, proto, " ".join(options), proto, hgroot, ud.module, ud.module)
|
||
|
else:
|
||
|
cmd = "%s clone %s %s://%s/%s %s" % (ud.basecmd, " ".join(options), proto, hgroot, ud.module, ud.module)
|
||
|
elif command == "pull":
|
||
|
# do not pass options list; limiting pull to rev causes the local
|
||
|
# repo not to contain it and immediately following "update" command
|
||
|
# will crash
|
||
|
if ud.user and ud.pswd:
|
||
|
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" pull" % (ud.basecmd, ud.user, ud.pswd, proto)
|
||
|
else:
|
||
|
cmd = "%s pull" % (ud.basecmd)
|
||
|
elif command == "update":
|
||
|
if ud.user and ud.pswd:
|
||
|
cmd = "%s --config auth.default.prefix=* --config auth.default.username=%s --config auth.default.password=%s --config \"auth.default.schemes=%s\" update -C %s" % (ud.basecmd, ud.user, ud.pswd, proto, " ".join(options))
|
||
|
else:
|
||
|
cmd = "%s update -C %s" % (ud.basecmd, " ".join(options))
|
||
|
else:
|
||
|
raise FetchError("Invalid hg command %s" % command, ud.url)
|
||
|
|
||
|
return cmd
|
||
|
|
||
|
def download(self, ud, d):
|
||
|
"""Fetch url"""
|
||
|
|
||
|
logger.debug(2, "Fetch: checking for module directory '" + ud.moddir + "'")
|
||
|
|
||
|
# If the checkout doesn't exist and the mirror tarball does, extract it
|
||
|
if not os.path.exists(ud.pkgdir) and os.path.exists(ud.fullmirror):
|
||
|
bb.utils.mkdirhier(ud.pkgdir)
|
||
|
runfetchcmd("tar -xzf %s" % (ud.fullmirror), d, workdir=ud.pkgdir)
|
||
|
|
||
|
if os.access(os.path.join(ud.moddir, '.hg'), os.R_OK):
|
||
|
# Found the source, check whether need pull
|
||
|
updatecmd = self._buildhgcommand(ud, d, "update")
|
||
|
logger.debug(1, "Running %s", updatecmd)
|
||
|
try:
|
||
|
runfetchcmd(updatecmd, d, workdir=ud.moddir)
|
||
|
except bb.fetch2.FetchError:
|
||
|
# Runnning pull in the repo
|
||
|
pullcmd = self._buildhgcommand(ud, d, "pull")
|
||
|
logger.info("Pulling " + ud.url)
|
||
|
# update sources there
|
||
|
logger.debug(1, "Running %s", pullcmd)
|
||
|
bb.fetch2.check_network_access(d, pullcmd, ud.url)
|
||
|
runfetchcmd(pullcmd, d, workdir=ud.moddir)
|
||
|
try:
|
||
|
os.unlink(ud.fullmirror)
|
||
|
except OSError as exc:
|
||
|
if exc.errno != errno.ENOENT:
|
||
|
raise
|
||
|
|
||
|
# No source found, clone it.
|
||
|
if not os.path.exists(ud.moddir):
|
||
|
fetchcmd = self._buildhgcommand(ud, d, "fetch")
|
||
|
logger.info("Fetch " + ud.url)
|
||
|
# check out sources there
|
||
|
bb.utils.mkdirhier(ud.pkgdir)
|
||
|
logger.debug(1, "Running %s", fetchcmd)
|
||
|
bb.fetch2.check_network_access(d, fetchcmd, ud.url)
|
||
|
runfetchcmd(fetchcmd, d, workdir=ud.pkgdir)
|
||
|
|
||
|
# Even when we clone (fetch), we still need to update as hg's clone
|
||
|
# won't checkout the specified revision if its on a branch
|
||
|
updatecmd = self._buildhgcommand(ud, d, "update")
|
||
|
logger.debug(1, "Running %s", updatecmd)
|
||
|
runfetchcmd(updatecmd, d, workdir=ud.moddir)
|
||
|
|
||
|
def clean(self, ud, d):
|
||
|
""" Clean the hg dir """
|
||
|
|
||
|
bb.utils.remove(ud.localpath, True)
|
||
|
bb.utils.remove(ud.fullmirror)
|
||
|
bb.utils.remove(ud.fullmirror + ".done")
|
||
|
|
||
|
def supports_srcrev(self):
|
||
|
return True
|
||
|
|
||
|
def _latest_revision(self, ud, d, name):
|
||
|
"""
|
||
|
Compute tip revision for the url
|
||
|
"""
|
||
|
bb.fetch2.check_network_access(d, self._buildhgcommand(ud, d, "info"), ud.url)
|
||
|
output = runfetchcmd(self._buildhgcommand(ud, d, "info"), d)
|
||
|
return output.strip()
|
||
|
|
||
|
def _build_revision(self, ud, d, name):
|
||
|
return ud.revision
|
||
|
|
||
|
def _revision_key(self, ud, d, name):
|
||
|
"""
|
||
|
Return a unique key for the url
|
||
|
"""
|
||
|
return "hg:" + ud.moddir
|
||
|
|
||
|
def build_mirror_data(self, ud, d):
|
||
|
# Generate a mirror tarball if needed
|
||
|
if ud.write_tarballs == "1" and not os.path.exists(ud.fullmirror):
|
||
|
# it's possible that this symlink points to read-only filesystem with PREMIRROR
|
||
|
if os.path.islink(ud.fullmirror):
|
||
|
os.unlink(ud.fullmirror)
|
||
|
|
||
|
logger.info("Creating tarball of hg repository")
|
||
|
runfetchcmd("tar -czf %s %s" % (ud.fullmirror, ud.module), d, workdir=ud.pkgdir)
|
||
|
runfetchcmd("touch %s.done" % (ud.fullmirror), d, workdir=ud.pkgdir)
|
||
|
|
||
|
def localpath(self, ud, d):
|
||
|
return ud.pkgdir
|
||
|
|
||
|
def unpack(self, ud, destdir, d):
|
||
|
"""
|
||
|
Make a local clone or export for the url
|
||
|
"""
|
||
|
|
||
|
revflag = "-r %s" % ud.revision
|
||
|
subdir = ud.parm.get("destsuffix", ud.module)
|
||
|
codir = "%s/%s" % (destdir, subdir)
|
||
|
|
||
|
scmdata = ud.parm.get("scmdata", "")
|
||
|
if scmdata != "nokeep":
|
||
|
if not os.access(os.path.join(codir, '.hg'), os.R_OK):
|
||
|
logger.debug(2, "Unpack: creating new hg repository in '" + codir + "'")
|
||
|
runfetchcmd("%s init %s" % (ud.basecmd, codir), d)
|
||
|
logger.debug(2, "Unpack: updating source in '" + codir + "'")
|
||
|
runfetchcmd("%s pull %s" % (ud.basecmd, ud.moddir), d, workdir=codir)
|
||
|
runfetchcmd("%s up -C %s" % (ud.basecmd, revflag), d, workdir=codir)
|
||
|
else:
|
||
|
logger.debug(2, "Unpack: extracting source to '" + codir + "'")
|
||
|
runfetchcmd("%s archive -t files %s %s" % (ud.basecmd, revflag, codir), d, workdir=ud.moddir)
|