git.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. # ex:ts=4:sw=4:sts=4:et
  2. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  3. """
  4. BitBake 'Fetch' git implementation
  5. """
  6. #Copyright (C) 2005 Richard Purdie
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License version 2 as
  10. # published by the Free Software Foundation.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License along
  18. # with this program; if not, write to the Free Software Foundation, Inc.,
  19. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. import os
  21. import bb
  22. from bb import data
  23. from bb.fetch2 import Fetch
  24. from bb.fetch2 import runfetchcmd
  25. from bb.fetch2 import logger
  26. class Git(Fetch):
  27. """Class to fetch a module or modules from git repositories"""
  28. def init(self, d):
  29. #
  30. # Only enable _sortable revision if the key is set
  31. #
  32. if bb.data.getVar("BB_GIT_CLONE_FOR_SRCREV", d, True):
  33. self._sortable_buildindex = self._sortable_buildindex_disabled
  34. def supports(self, url, ud, d):
  35. """
  36. Check to see if a given url can be fetched with git.
  37. """
  38. return ud.type in ['git']
  39. def urldata_init(self, ud, d):
  40. """
  41. init git specific variable within url data
  42. so that the git method like latest_revision() can work
  43. """
  44. if 'protocol' in ud.parm:
  45. ud.proto = ud.parm['protocol']
  46. elif not ud.host:
  47. ud.proto = 'file'
  48. else:
  49. ud.proto = "rsync"
  50. ud.nocheckout = False
  51. if 'nocheckout' in ud.parm:
  52. ud.nocheckout = True
  53. branches = ud.parm.get("branch", "master").split(',')
  54. if len(branches) != len(ud.names):
  55. raise bb.fetch2.ParameterError("SRC_URI (%) name and branch number mismatch" % ud.url)
  56. ud.branches = {}
  57. for name in ud.names:
  58. branch = branches[ud.names.index(name)]
  59. ud.branches[name] = branch
  60. gitsrcname = '%s%s' % (ud.host, ud.path.replace('/', '.'))
  61. ud.mirrortarball = 'git2_%s.tar.gz' % (gitsrcname)
  62. ud.clonedir = os.path.join(data.expand('${GITDIR}', d), gitsrcname)
  63. ud.basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
  64. for name in ud.names:
  65. if not ud.revisions[name] or ud.revisions[name] == "master":
  66. ud.revisions[name] = self.latest_revision(url, ud, d, name)
  67. ud.localfile = ud.mirrortarball
  68. def localpath(self, url, ud, d):
  69. return os.path.join(data.getVar("DL_DIR", d, True), ud.localfile)
  70. def forcefetch(self, url, ud, d):
  71. for name in ud.names:
  72. if not self._contains_ref(ud.revisions[name], d):
  73. return True
  74. return False
  75. def try_premirror(self, u, ud, d):
  76. if 'noclone' in ud.parm:
  77. return False
  78. if os.path.exists(ud.clonedir):
  79. return False
  80. if os.path.exists(ud.localpath):
  81. return False
  82. return True
  83. def download(self, loc, ud, d):
  84. """Fetch url"""
  85. if ud.user:
  86. username = ud.user + '@'
  87. else:
  88. username = ""
  89. repofile = os.path.join(data.getVar("DL_DIR", d, 1), ud.mirrortarball)
  90. ud.repochanged = not os.path.exists(repofile)
  91. # If the checkout doesn't exist and the mirror tarball does, extract it
  92. if not os.path.exists(ud.clonedir) and os.path.exists(repofile):
  93. bb.mkdirhier(ud.clonedir)
  94. os.chdir(ud.clonedir)
  95. runfetchcmd("tar -xzf %s" % (repofile), d)
  96. # If the repo still doesn't exist, fallback to cloning it
  97. if not os.path.exists(ud.clonedir):
  98. bb.fetch2.check_network_access(d, "git clone --bare %s%s" % (ud.host, ud.path))
  99. runfetchcmd("%s clone --bare %s://%s%s%s %s" % (ud.basecmd, ud.proto, username, ud.host, ud.path, ud.clonedir), d)
  100. os.chdir(ud.clonedir)
  101. # Update the checkout if needed
  102. needupdate = False
  103. for name in ud.names:
  104. if not self._contains_ref(ud.revisions[name], d):
  105. needupdate = True
  106. if needupdate:
  107. bb.fetch2.check_network_access(d, "git fetch %s%s" % (ud.host, ud.path))
  108. try:
  109. runfetchcmd("%s remote prune origin" % ud.basecmd, d)
  110. runfetchcmd("%s remote rm origin" % ud.basecmd, d)
  111. except bb.fetch2.FetchError:
  112. logger.debug(1, "No Origin")
  113. runfetchcmd("%s remote add origin %s://%s%s%s" % (ud.basecmd, ud.proto, username, ud.host, ud.path), d)
  114. runfetchcmd("%s fetch --all -t" % ud.basecmd, d)
  115. runfetchcmd("%s prune-packed" % ud.basecmd, d)
  116. runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d)
  117. ud.repochanged = True
  118. def build_mirror_data(self, url, ud, d):
  119. # Generate a mirror tarball if needed
  120. repofile = os.path.join(data.getVar("DL_DIR", d, 1), ud.mirrortarball)
  121. os.chdir(ud.clonedir)
  122. mirror_tarballs = data.getVar("BB_GENERATE_MIRROR_TARBALLS", d, True)
  123. if mirror_tarballs != "0" and ud.repochanged:
  124. logger.info("Creating tarball of git repository")
  125. runfetchcmd("tar -czf %s %s" % (repofile, os.path.join(".") ), d)
  126. def unpack(self, ud, destdir, d):
  127. """ unpack the downloaded src to destdir"""
  128. subdir = ud.parm.get("subpath", "")
  129. if subdir != "":
  130. readpathspec = ":%s" % (subdir)
  131. else:
  132. readpathspec = ""
  133. destdir = os.path.join(destdir, "git/")
  134. if os.path.exists(destdir):
  135. bb.utils.prunedir(destdir)
  136. runfetchcmd("git clone -s -n %s %s" % (ud.clonedir, destdir), d)
  137. if not ud.nocheckout:
  138. os.chdir(destdir)
  139. runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d)
  140. runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d)
  141. return True
  142. def supports_srcrev(self):
  143. return True
  144. def _contains_ref(self, tag, d):
  145. basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
  146. output = runfetchcmd("%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (basecmd, tag), d, quiet=True)
  147. return output.split()[0] != "0"
  148. def _revision_key(self, url, ud, d, name):
  149. """
  150. Return a unique key for the url
  151. """
  152. return "git:" + ud.host + ud.path.replace('/', '.') + ud.branches[name]
  153. def _latest_revision(self, url, ud, d, name):
  154. """
  155. Compute the HEAD revision for the url
  156. """
  157. if ud.user:
  158. username = ud.user + '@'
  159. else:
  160. username = ""
  161. bb.fetch2.check_network_access(d, "git ls-remote %s%s %s" % (ud.host, ud.path, ud.branches[name]))
  162. basecmd = data.getVar("FETCHCMD_git", d, True) or "git"
  163. cmd = "%s ls-remote %s://%s%s%s %s" % (basecmd, ud.proto, username, ud.host, ud.path, ud.branches[name])
  164. output = runfetchcmd(cmd, d, True)
  165. if not output:
  166. raise bb.fetch2.FetchError("Fetch command %s gave empty output\n" % (cmd))
  167. return output.split()[0]
  168. def _build_revision(self, url, ud, d, name):
  169. return ud.revisions[name]
  170. def _sortable_buildindex_disabled(self, url, ud, d, rev):
  171. """
  172. Return a suitable buildindex for the revision specified. This is done by counting revisions
  173. using "git rev-list" which may or may not work in different circumstances.
  174. """
  175. cwd = os.getcwd()
  176. # Check if we have the rev already
  177. if not os.path.exists(ud.clonedir):
  178. print("no repo")
  179. self.download(None, ud, d)
  180. if not os.path.exists(ud.clonedir):
  181. logger.error("GIT repository for %s doesn't exist in %s, cannot get sortable buildnumber, using old value", url, ud.clonedir)
  182. return None
  183. os.chdir(ud.clonedir)
  184. if not self._contains_ref(rev, d):
  185. self.download(None, ud, d)
  186. output = runfetchcmd("%s rev-list %s -- 2> /dev/null | wc -l" % (ud.basecmd, rev), d, quiet=True)
  187. os.chdir(cwd)
  188. buildindex = "%s" % output.split()[0]
  189. logger.debug(1, "GIT repository for %s in %s is returning %s revisions in rev-list before %s", url, ud.clonedir, buildindex, rev)
  190. return buildindex