blob: c622771d21aada990c0f2a875d04b341dad05869 [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
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023from bb.fetch2.git import Git
24from bb.fetch2 import runfetchcmd
25from bb.fetch2 import logger
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080026from bb.fetch2 import Fetch
27from bb.fetch2 import BBFetchException
Patrick Williamsc124f4f2015-09-15 14:41:29 -050028
29class GitSM(Git):
30 def supports(self, ud, d):
31 """
32 Check to see if a given url can be fetched with git.
33 """
34 return ud.type in ['gitsm']
35
Brad Bishopf8caae32019-03-25 13:13:56 -040036 def process_submodules(self, ud, workdir, function, d):
37 """
38 Iterate over all of the submodules in this repository and execute
39 the 'function' for each of them.
40 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080042 submodules = []
43 paths = {}
Brad Bishopf8caae32019-03-25 13:13:56 -040044 revision = {}
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080045 uris = {}
Brad Bishopf8caae32019-03-25 13:13:56 -040046 subrevision = {}
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080047
Brad Bishopf8caae32019-03-25 13:13:56 -040048 def parse_gitmodules(gitmodules):
49 modules = {}
50 module = ""
51 for line in gitmodules.splitlines():
52 if line.startswith('[submodule'):
53 module = line.split('"')[1]
54 modules[module] = {}
55 elif module and line.strip().startswith('path'):
56 path = line.split('=')[1].strip()
57 modules[module]['path'] = path
58 elif module and line.strip().startswith('url'):
59 url = line.split('=')[1].strip()
60 modules[module]['url'] = url
61 return modules
62
63 # Collect the defined submodules, and their attributes
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080064 for name in ud.names:
65 try:
Brad Bishopf8caae32019-03-25 13:13:56 -040066 gitmodules = runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=workdir)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080067 except:
68 # No submodules to update
69 continue
70
Brad Bishopf8caae32019-03-25 13:13:56 -040071 for m, md in parse_gitmodules(gitmodules).items():
72 try:
73 module_hash = runfetchcmd("%s ls-tree -z -d %s %s" % (ud.basecmd, ud.revisions[name], md['path']), d, quiet=True, workdir=workdir)
74 except:
75 # If the command fails, we don't have a valid file to check. If it doesn't
76 # fail -- it still might be a failure, see next check...
77 module_hash = ""
78
79 if not module_hash:
80 logger.debug(1, "submodule %s is defined, but is not initialized in the repository. Skipping", m)
81 continue
82
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080083 submodules.append(m)
84 paths[m] = md['path']
Brad Bishopf8caae32019-03-25 13:13:56 -040085 revision[m] = ud.revisions[name]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080086 uris[m] = md['url']
Brad Bishopf8caae32019-03-25 13:13:56 -040087 subrevision[m] = module_hash.split()[2]
88
89 # Convert relative to absolute uri based on parent uri
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080090 if uris[m].startswith('..'):
91 newud = copy.copy(ud)
Brad Bishopf8caae32019-03-25 13:13:56 -040092 newud.path = os.path.realpath(os.path.join(newud.path, uris[m]))
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080093 uris[m] = Git._get_repo_url(self, newud)
94
95 for module in submodules:
Brad Bishopf8caae32019-03-25 13:13:56 -040096 # Translate the module url into a SRC_URI
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080097
Brad Bishopf8caae32019-03-25 13:13:56 -040098 if "://" in uris[module]:
99 # Properly formated URL already
100 proto = uris[module].split(':', 1)[0]
101 url = uris[module].replace('%s:' % proto, 'gitsm:', 1)
102 else:
103 if ":" in uris[module]:
104 # Most likely an SSH style reference
105 proto = "ssh"
106 if ":/" in uris[module]:
107 # Absolute reference, easy to convert..
108 url = "gitsm://" + uris[module].replace(':/', '/', 1)
109 else:
110 # Relative reference, no way to know if this is right!
111 logger.warning("Submodule included by %s refers to relative ssh reference %s. References may fail if not absolute." % (ud.url, uris[module]))
112 url = "gitsm://" + uris[module].replace(':', '/', 1)
113 else:
114 # This has to be a file reference
115 proto = "file"
116 url = "gitsm://" + uris[module]
117
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800118 url += ';protocol=%s' % proto
119 url += ";name=%s" % module
Brad Bishop393846f2019-05-20 12:24:11 -0400120 url += ";subpath=%s" % module
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800121
122 ld = d.createCopy()
123 # Not necessary to set SRC_URI, since we're passing the URI to
124 # Fetch.
125 #ld.setVar('SRC_URI', url)
Brad Bishopf8caae32019-03-25 13:13:56 -0400126 ld.setVar('SRCREV_%s' % module, subrevision[module])
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800127
128 # Workaround for issues with SRCPV/SRCREV_FORMAT errors
129 # error refer to 'multiple' repositories. Only the repository
130 # in the original SRC_URI actually matters...
131 ld.setVar('SRCPV', d.getVar('SRCPV'))
132 ld.setVar('SRCREV_FORMAT', module)
133
Brad Bishopf8caae32019-03-25 13:13:56 -0400134 function(ud, url, module, paths[module], ld)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800135
Brad Bishopf8caae32019-03-25 13:13:56 -0400136 return submodules != []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500137
Brad Bishop19323692019-04-05 15:28:33 -0400138 def need_update(self, ud, d):
139 if Git.need_update(self, ud, d):
140 return True
141
142 try:
143 # Check for the nugget dropped by the download operation
144 known_srcrevs = runfetchcmd("%s config --get-all bitbake.srcrev" % \
145 (ud.basecmd), d, workdir=ud.clonedir)
146
147 if ud.revisions[ud.names[0]] not in known_srcrevs.split():
148 return True
149 except bb.fetch2.FetchError:
150 # No srcrev nuggets, so this is new and needs to be updated
151 return True
152
153 return False
154
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500155 def download(self, ud, d):
Brad Bishopf8caae32019-03-25 13:13:56 -0400156 def download_submodule(ud, url, module, modpath, d):
157 url += ";bareclone=1;nobranch=1"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500158
Brad Bishopf8caae32019-03-25 13:13:56 -0400159 # Is the following still needed?
160 #url += ";nocheckout=1"
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800161
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800162 try:
Brad Bishopf8caae32019-03-25 13:13:56 -0400163 newfetch = Fetch([url], d, cache=False)
164 newfetch.download()
Brad Bishop19323692019-04-05 15:28:33 -0400165 # Drop a nugget to add each of the srcrevs we've fetched (used by need_update)
166 runfetchcmd("%s config --add bitbake.srcrev %s" % \
167 (ud.basecmd, ud.revisions[ud.names[0]]), d, workdir=ud.clonedir)
Brad Bishopf8caae32019-03-25 13:13:56 -0400168 except Exception as e:
169 logger.error('gitsm: submodule download failed: %s %s' % (type(e).__name__, str(e)))
170 raise
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800171
Brad Bishopf8caae32019-03-25 13:13:56 -0400172 Git.download(self, ud, d)
173 self.process_submodules(ud, ud.clonedir, download_submodule, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500174
175 def unpack(self, ud, destdir, d):
Brad Bishopf8caae32019-03-25 13:13:56 -0400176 def unpack_submodules(ud, url, module, modpath, d):
177 url += ";bareclone=1;nobranch=1"
178
179 # Figure out where we clone over the bare submodules...
180 if ud.bareclone:
181 repo_conf = ud.destdir
182 else:
183 repo_conf = os.path.join(ud.destdir, '.git')
184
185 try:
186 newfetch = Fetch([url], d, cache=False)
Brad Bishop393846f2019-05-20 12:24:11 -0400187 newfetch.unpack(root=os.path.dirname(os.path.join(repo_conf, 'modules', module)))
Brad Bishopf8caae32019-03-25 13:13:56 -0400188 except Exception as e:
189 logger.error('gitsm: submodule unpack failed: %s %s' % (type(e).__name__, str(e)))
190 raise
191
192 local_path = newfetch.localpath(url)
193
194 # Correct the submodule references to the local download version...
195 runfetchcmd("%(basecmd)s config submodule.%(module)s.url %(url)s" % {'basecmd': ud.basecmd, 'module': module, 'url' : local_path}, d, workdir=ud.destdir)
196
197 if ud.shallow:
198 runfetchcmd("%(basecmd)s config submodule.%(module)s.shallow true" % {'basecmd': ud.basecmd, 'module': module}, d, workdir=ud.destdir)
199
200 # Ensure the submodule repository is NOT set to bare, since we're checking it out...
201 try:
Brad Bishop393846f2019-05-20 12:24:11 -0400202 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 -0400203 except:
Brad Bishop393846f2019-05-20 12:24:11 -0400204 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 -0400205 raise
206
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207 Git.unpack(self, ud, destdir, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500208
Brad Bishopf8caae32019-03-25 13:13:56 -0400209 ret = self.process_submodules(ud, ud.destdir, unpack_submodules, d)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800210
Brad Bishopf8caae32019-03-25 13:13:56 -0400211 if not ud.bareclone and ret:
Brad Bishop393846f2019-05-20 12:24:11 -0400212 # All submodules should already be downloaded and configured in the tree. This simply sets
213 # up the configuration and checks out the files. The main project config should remain
214 # unmodified, and no download from the internet should occur.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800215 runfetchcmd("%s submodule update --recursive --no-fetch" % (ud.basecmd), d, quiet=True, workdir=ud.destdir)