blob: f7f3af7212555d2b6c26d76abdf3e88bb51f7d0c [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001"""
2BitBake 'Fetch' git submodules implementation
3
4Inherits from and extends the Git fetcher to retrieve submodules of a git repository
5after cloning.
6
7SRC_URI = "gitsm://<see Git fetcher for syntax>"
8
9See the Git fetcher, git://, for usage documentation.
10
11NOTE: Switching a SRC_URI from "git://" to "gitsm://" requires a clean of your recipe.
12
13"""
14
15# Copyright (C) 2013 Richard Purdie
16#
Brad Bishopc342db32019-05-15 21:57:59 -040017# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -050018#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019
20import os
21import bb
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080022import copy
Andrew Geissler82c905d2020-04-13 13:39:40 -050023import shutil
24import tempfile
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025from bb.fetch2.git import Git
26from bb.fetch2 import runfetchcmd
27from bb.fetch2 import logger
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080028from bb.fetch2 import Fetch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029
30class GitSM(Git):
31 def supports(self, ud, d):
32 """
33 Check to see if a given url can be fetched with git.
34 """
35 return ud.type in ['gitsm']
36
Brad Bishopf8caae32019-03-25 13:13:56 -040037 def process_submodules(self, ud, workdir, function, d):
38 """
39 Iterate over all of the submodules in this repository and execute
40 the 'function' for each of them.
41 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080043 submodules = []
44 paths = {}
Brad Bishopf8caae32019-03-25 13:13:56 -040045 revision = {}
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080046 uris = {}
Brad Bishopf8caae32019-03-25 13:13:56 -040047 subrevision = {}
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080048
Brad Bishopf8caae32019-03-25 13:13:56 -040049 def parse_gitmodules(gitmodules):
50 modules = {}
51 module = ""
52 for line in gitmodules.splitlines():
53 if line.startswith('[submodule'):
54 module = line.split('"')[1]
55 modules[module] = {}
56 elif module and line.strip().startswith('path'):
57 path = line.split('=')[1].strip()
58 modules[module]['path'] = path
59 elif module and line.strip().startswith('url'):
60 url = line.split('=')[1].strip()
61 modules[module]['url'] = url
62 return modules
63
64 # Collect the defined submodules, and their attributes
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080065 for name in ud.names:
66 try:
Brad Bishopf8caae32019-03-25 13:13:56 -040067 gitmodules = runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=workdir)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080068 except:
69 # No submodules to update
70 continue
71
Brad Bishopf8caae32019-03-25 13:13:56 -040072 for m, md in parse_gitmodules(gitmodules).items():
73 try:
74 module_hash = runfetchcmd("%s ls-tree -z -d %s %s" % (ud.basecmd, ud.revisions[name], md['path']), d, quiet=True, workdir=workdir)
75 except:
76 # If the command fails, we don't have a valid file to check. If it doesn't
77 # fail -- it still might be a failure, see next check...
78 module_hash = ""
79
80 if not module_hash:
Andrew Geisslerd1e89492021-02-12 15:35:20 -060081 logger.debug("submodule %s is defined, but is not initialized in the repository. Skipping", m)
Brad Bishopf8caae32019-03-25 13:13:56 -040082 continue
83
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080084 submodules.append(m)
85 paths[m] = md['path']
Brad Bishopf8caae32019-03-25 13:13:56 -040086 revision[m] = ud.revisions[name]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080087 uris[m] = md['url']
Brad Bishopf8caae32019-03-25 13:13:56 -040088 subrevision[m] = module_hash.split()[2]
89
90 # Convert relative to absolute uri based on parent uri
Andrew Geissler615f2f12022-07-15 14:00:58 -050091 if uris[m].startswith('..') or uris[m].startswith('./'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080092 newud = copy.copy(ud)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -060093 newud.path = os.path.normpath(os.path.join(newud.path, uris[m]))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080094 uris[m] = Git._get_repo_url(self, newud)
95
96 for module in submodules:
Brad Bishopf8caae32019-03-25 13:13:56 -040097 # Translate the module url into a SRC_URI
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080098
Brad Bishopf8caae32019-03-25 13:13:56 -040099 if "://" in uris[module]:
100 # Properly formated URL already
101 proto = uris[module].split(':', 1)[0]
102 url = uris[module].replace('%s:' % proto, 'gitsm:', 1)
103 else:
104 if ":" in uris[module]:
105 # Most likely an SSH style reference
106 proto = "ssh"
107 if ":/" in uris[module]:
108 # Absolute reference, easy to convert..
109 url = "gitsm://" + uris[module].replace(':/', '/', 1)
110 else:
111 # Relative reference, no way to know if this is right!
112 logger.warning("Submodule included by %s refers to relative ssh reference %s. References may fail if not absolute." % (ud.url, uris[module]))
113 url = "gitsm://" + uris[module].replace(':', '/', 1)
114 else:
115 # This has to be a file reference
116 proto = "file"
117 url = "gitsm://" + uris[module]
Patrick Williams7784c422022-11-17 07:29:11 -0600118 if url.endswith("{}{}".format(ud.host, ud.path)):
Patrick Williams92b42cb2022-09-03 06:53:57 -0500119 raise bb.fetch2.FetchError("Submodule refers to the parent repository. This will cause deadlock situation in current version of Bitbake." \
120 "Consider using git fetcher instead.")
Brad Bishopf8caae32019-03-25 13:13:56 -0400121
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800122 url += ';protocol=%s' % proto
123 url += ";name=%s" % module
Brad Bishop393846f2019-05-20 12:24:11 -0400124 url += ";subpath=%s" % module
Patrick Williams7784c422022-11-17 07:29:11 -0600125 url += ";nobranch=1"
Andrew Geissler5082cc72023-09-11 08:41:39 -0400126 url += ";lfs=%s" % self._need_lfs(ud)
Patrick Williams2a254922023-08-11 09:48:11 -0500127 # Note that adding "user=" here to give credentials to the
128 # submodule is not supported. Since using SRC_URI to give git://
129 # URL a password is not supported, one have to use one of the
130 # recommended way (eg. ~/.netrc or SSH config) which does specify
131 # the user (See comment in git.py).
132 # So, we will not take patches adding "user=" support here.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800133
134 ld = d.createCopy()
135 # Not necessary to set SRC_URI, since we're passing the URI to
136 # Fetch.
137 #ld.setVar('SRC_URI', url)
Brad Bishopf8caae32019-03-25 13:13:56 -0400138 ld.setVar('SRCREV_%s' % module, subrevision[module])
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800139
140 # Workaround for issues with SRCPV/SRCREV_FORMAT errors
141 # error refer to 'multiple' repositories. Only the repository
142 # in the original SRC_URI actually matters...
143 ld.setVar('SRCPV', d.getVar('SRCPV'))
144 ld.setVar('SRCREV_FORMAT', module)
145
Andrew Geissler82c905d2020-04-13 13:39:40 -0500146 function(ud, url, module, paths[module], workdir, ld)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800147
Brad Bishopf8caae32019-03-25 13:13:56 -0400148 return submodules != []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500149
Brad Bishop19323692019-04-05 15:28:33 -0400150 def need_update(self, ud, d):
151 if Git.need_update(self, ud, d):
152 return True
153
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500154 need_update_list = []
155 def need_update_submodule(ud, url, module, modpath, workdir, d):
156 url += ";bareclone=1;nobranch=1"
157
158 try:
159 newfetch = Fetch([url], d, cache=False)
160 new_ud = newfetch.ud[url]
161 if new_ud.method.need_update(new_ud, d):
162 need_update_list.append(modpath)
163 except Exception as e:
164 logger.error('gitsm: submodule update check failed: %s %s' % (type(e).__name__, str(e)))
165 need_update_result = True
166
167 # If we're using a shallow mirror tarball it needs to be unpacked
168 # temporarily so that we can examine the .gitmodules file
169 if ud.shallow and os.path.exists(ud.fullshallow) and not os.path.exists(ud.clonedir):
170 tmpdir = tempfile.mkdtemp(dir=d.getVar("DL_DIR"))
171 runfetchcmd("tar -xzf %s" % ud.fullshallow, d, workdir=tmpdir)
172 self.process_submodules(ud, tmpdir, need_update_submodule, d)
173 shutil.rmtree(tmpdir)
174 else:
175 self.process_submodules(ud, ud.clonedir, need_update_submodule, d)
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500176
Andrew Geissler595f6302022-01-24 19:11:47 +0000177 if need_update_list:
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600178 logger.debug('gitsm: Submodules requiring update: %s' % (' '.join(need_update_list)))
Brad Bishop19323692019-04-05 15:28:33 -0400179 return True
180
181 return False
182
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500183 def download(self, ud, d):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500184 def download_submodule(ud, url, module, modpath, workdir, d):
Brad Bishopf8caae32019-03-25 13:13:56 -0400185 url += ";bareclone=1;nobranch=1"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500186
Brad Bishopf8caae32019-03-25 13:13:56 -0400187 # Is the following still needed?
188 #url += ";nocheckout=1"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800189
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800190 try:
Brad Bishopf8caae32019-03-25 13:13:56 -0400191 newfetch = Fetch([url], d, cache=False)
192 newfetch.download()
193 except Exception as e:
194 logger.error('gitsm: submodule download failed: %s %s' % (type(e).__name__, str(e)))
195 raise
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800196
Brad Bishopf8caae32019-03-25 13:13:56 -0400197 Git.download(self, ud, d)
Andrew Geissler82c905d2020-04-13 13:39:40 -0500198
199 # If we're using a shallow mirror tarball it needs to be unpacked
200 # temporarily so that we can examine the .gitmodules file
201 if ud.shallow and os.path.exists(ud.fullshallow) and self.need_update(ud, d):
202 tmpdir = tempfile.mkdtemp(dir=d.getVar("DL_DIR"))
203 runfetchcmd("tar -xzf %s" % ud.fullshallow, d, workdir=tmpdir)
204 self.process_submodules(ud, tmpdir, download_submodule, d)
205 shutil.rmtree(tmpdir)
206 else:
207 self.process_submodules(ud, ud.clonedir, download_submodule, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500208
209 def unpack(self, ud, destdir, d):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500210 def unpack_submodules(ud, url, module, modpath, workdir, d):
Brad Bishopf8caae32019-03-25 13:13:56 -0400211 url += ";bareclone=1;nobranch=1"
212
213 # Figure out where we clone over the bare submodules...
214 if ud.bareclone:
215 repo_conf = ud.destdir
216 else:
217 repo_conf = os.path.join(ud.destdir, '.git')
218
219 try:
220 newfetch = Fetch([url], d, cache=False)
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600221 # modpath is needed by unpack tracer to calculate submodule
222 # checkout dir
223 new_ud = newfetch.ud[url]
224 new_ud.modpath = modpath
Brad Bishop393846f2019-05-20 12:24:11 -0400225 newfetch.unpack(root=os.path.dirname(os.path.join(repo_conf, 'modules', module)))
Brad Bishopf8caae32019-03-25 13:13:56 -0400226 except Exception as e:
227 logger.error('gitsm: submodule unpack failed: %s %s' % (type(e).__name__, str(e)))
228 raise
229
230 local_path = newfetch.localpath(url)
231
232 # Correct the submodule references to the local download version...
233 runfetchcmd("%(basecmd)s config submodule.%(module)s.url %(url)s" % {'basecmd': ud.basecmd, 'module': module, 'url' : local_path}, d, workdir=ud.destdir)
234
235 if ud.shallow:
236 runfetchcmd("%(basecmd)s config submodule.%(module)s.shallow true" % {'basecmd': ud.basecmd, 'module': module}, d, workdir=ud.destdir)
237
238 # Ensure the submodule repository is NOT set to bare, since we're checking it out...
239 try:
Brad Bishop393846f2019-05-20 12:24:11 -0400240 runfetchcmd("%s config core.bare false" % (ud.basecmd), d, quiet=True, workdir=os.path.join(repo_conf, 'modules', module))
Brad Bishopf8caae32019-03-25 13:13:56 -0400241 except:
Brad Bishop393846f2019-05-20 12:24:11 -0400242 logger.error("Unable to set git config core.bare to false for %s" % os.path.join(repo_conf, 'modules', module))
Brad Bishopf8caae32019-03-25 13:13:56 -0400243 raise
244
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245 Git.unpack(self, ud, destdir, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500246
Brad Bishopf8caae32019-03-25 13:13:56 -0400247 ret = self.process_submodules(ud, ud.destdir, unpack_submodules, d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800248
Brad Bishopf8caae32019-03-25 13:13:56 -0400249 if not ud.bareclone and ret:
Andrew Geissler5082cc72023-09-11 08:41:39 -0400250 # All submodules should already be downloaded and configured in the tree. This simply
251 # sets up the configuration and checks out the files. The main project config should
252 # remain unmodified, and no download from the internet should occur. As such, lfs smudge
253 # should also be skipped as these files were already smudged in the fetch stage if lfs
254 # was enabled.
255 runfetchcmd("GIT_LFS_SKIP_SMUDGE=1 %s submodule update --recursive --no-fetch" % (ud.basecmd), d, quiet=True, workdir=ud.destdir)
Andrew Geissler4ed12e12020-06-05 18:00:41 -0500256
257 def implicit_urldata(self, ud, d):
258 import shutil, subprocess, tempfile
259
260 urldata = []
261 def add_submodule(ud, url, module, modpath, workdir, d):
262 url += ";bareclone=1;nobranch=1"
263 newfetch = Fetch([url], d, cache=False)
264 urldata.extend(newfetch.expanded_urldata())
265
266 # If we're using a shallow mirror tarball it needs to be unpacked
267 # temporarily so that we can examine the .gitmodules file
268 if ud.shallow and os.path.exists(ud.fullshallow) and ud.method.need_update(ud, d):
269 tmpdir = tempfile.mkdtemp(dir=d.getVar("DL_DIR"))
270 subprocess.check_call("tar -xzf %s" % ud.fullshallow, cwd=tmpdir, shell=True)
271 self.process_submodules(ud, tmpdir, add_submodule, d)
272 shutil.rmtree(tmpdir)
273 else:
274 self.process_submodules(ud, ud.clonedir, add_submodule, d)
275
276 return urldata