diff options
-rw-r--r-- | bitbake/lib/bb/fetch2/gitsm.py | 232 | ||||
-rw-r--r-- | bitbake/lib/bb/tests/fetch.py | 12 |
2 files changed, 98 insertions, 146 deletions
diff --git a/bitbake/lib/bb/fetch2/gitsm.py b/bitbake/lib/bb/fetch2/gitsm.py index 83571f834b..86198ee6cd 100644 --- a/bitbake/lib/bb/fetch2/gitsm.py +++ b/bitbake/lib/bb/fetch2/gitsm.py | |||
@@ -45,85 +45,96 @@ class GitSM(Git): | |||
45 | """ | 45 | """ |
46 | return ud.type in ['gitsm'] | 46 | return ud.type in ['gitsm'] |
47 | 47 | ||
48 | @staticmethod | 48 | def process_submodules(self, ud, workdir, function, d): |
49 | def parse_gitmodules(gitmodules): | 49 | """ |
50 | modules = {} | 50 | Iterate over all of the submodules in this repository and execute |
51 | module = "" | 51 | the 'function' for each of them. |
52 | for line in gitmodules.splitlines(): | 52 | """ |
53 | if line.startswith('[submodule'): | 53 | |
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 | def update_submodules(self, ud, d): | ||
65 | submodules = [] | 54 | submodules = [] |
66 | paths = {} | 55 | paths = {} |
67 | revision = {} | 56 | revision = {} |
68 | uris = {} | 57 | uris = {} |
69 | local_paths = {} | 58 | subrevision = {} |
70 | 59 | ||
60 | def parse_gitmodules(gitmodules): | ||
61 | modules = {} | ||
62 | module = "" | ||
63 | for line in gitmodules.splitlines(): | ||
64 | if line.startswith('[submodule'): | ||
65 | module = line.split('"')[1] | ||
66 | modules[module] = {} | ||
67 | elif module and line.strip().startswith('path'): | ||
68 | path = line.split('=')[1].strip() | ||
69 | modules[module]['path'] = path | ||
70 | elif module and line.strip().startswith('url'): | ||
71 | url = line.split('=')[1].strip() | ||
72 | modules[module]['url'] = url | ||
73 | return modules | ||
74 | |||
75 | # Collect the defined submodules, and their attributes | ||
71 | for name in ud.names: | 76 | for name in ud.names: |
72 | try: | 77 | try: |
73 | gitmodules = runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=ud.clonedir) | 78 | gitmodules = runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=workdir) |
74 | except: | 79 | except: |
75 | # No submodules to update | 80 | # No submodules to update |
76 | continue | 81 | continue |
77 | 82 | ||
78 | for m, md in self.parse_gitmodules(gitmodules).items(): | 83 | for m, md in parse_gitmodules(gitmodules).items(): |
84 | try: | ||
85 | module_hash = runfetchcmd("%s ls-tree -z -d %s %s" % (ud.basecmd, ud.revisions[name], md['path']), d, quiet=True, workdir=workdir) | ||
86 | except: | ||
87 | # If the command fails, we don't have a valid file to check. If it doesn't | ||
88 | # fail -- it still might be a failure, see next check... | ||
89 | module_hash = "" | ||
90 | |||
91 | if not module_hash: | ||
92 | logger.debug(1, "submodule %s is defined, but is not initialized in the repository. Skipping", m) | ||
93 | continue | ||
94 | |||
79 | submodules.append(m) | 95 | submodules.append(m) |
80 | paths[m] = md['path'] | 96 | paths[m] = md['path'] |
81 | revision[m] = ud.revisions[name] | 97 | revision[m] = ud.revisions[name] |
82 | uris[m] = md['url'] | 98 | uris[m] = md['url'] |
83 | if uris[m].startswith('..'): | 99 | subrevision[m] = module_hash.split()[2] |
84 | newud = copy.copy(ud) | ||
85 | newud.path = os.path.realpath(os.path.join(newud.path, md['url'])) | ||
86 | uris[m] = Git._get_repo_url(self, newud) | ||
87 | 100 | ||
88 | for module in submodules: | 101 | for module in submodules: |
89 | try: | 102 | # Translate the module url into a SRC_URI |
90 | module_hash = runfetchcmd("%s ls-tree -z -d %s %s" % (ud.basecmd, revision[module], paths[module]), d, quiet=True, workdir=ud.clonedir) | ||
91 | except: | ||
92 | # If the command fails, we don't have a valid file to check. If it doesn't | ||
93 | # fail -- it still might be a failure, see next check... | ||
94 | module_hash = "" | ||
95 | 103 | ||
96 | if not module_hash: | 104 | if "://" in uris[module]: |
97 | logger.debug(1, "submodule %s is defined, but is not initialized in the repository. Skipping", module) | 105 | # Properly formated URL already |
98 | continue | 106 | proto = uris[module].split(':', 1)[0] |
99 | 107 | url = uris[module].replace('%s:' % proto, 'gitsm:', 1) | |
100 | module_hash = module_hash.split()[2] | 108 | else: |
101 | |||
102 | # Build new SRC_URI | ||
103 | if "://" not in uris[module]: | ||
104 | # It's ssh if the format does NOT have "://", but has a ':' | ||
105 | if ":" in uris[module]: | 109 | if ":" in uris[module]: |
110 | # Most likely an SSH style reference | ||
106 | proto = "ssh" | 111 | proto = "ssh" |
107 | if ":/" in uris[module]: | 112 | if ":/" in uris[module]: |
113 | # Absolute reference, easy to convert.. | ||
108 | url = "gitsm://" + uris[module].replace(':/', '/', 1) | 114 | url = "gitsm://" + uris[module].replace(':/', '/', 1) |
109 | else: | 115 | else: |
116 | # Relative reference, no way to know if this is right! | ||
117 | logger.warning("Submodule included by %s refers to relative ssh reference %s. References may fail if not absolute." % (ud.url, uris[module])) | ||
110 | url = "gitsm://" + uris[module].replace(':', '/', 1) | 118 | url = "gitsm://" + uris[module].replace(':', '/', 1) |
111 | else: # Fall back to 'file' if there is no ':' | 119 | else: |
120 | # This has to be a file reference | ||
112 | proto = "file" | 121 | proto = "file" |
113 | url = "gitsm://" + uris[module] | 122 | url = "gitsm://" + uris[module] |
114 | else: | 123 | if uris[module].startswith('..'): |
115 | proto = uris[module].split(':', 1)[0] | 124 | # Local on disk relative reference |
116 | url = uris[module].replace('%s:' % proto, 'gitsm:', 1) | 125 | newud = copy.copy(ud) |
126 | newud.path = os.path.realpath(os.path.join(newud.path, md['url'])) | ||
127 | url = "gitsm://" + Git._get_repo_url(self, newud) | ||
117 | 128 | ||
118 | url += ';protocol=%s' % proto | 129 | url += ';protocol=%s' % proto |
119 | url += ";name=%s" % module | 130 | url += ";name=%s" % module |
120 | url += ";bareclone=1;nocheckout=1;nobranch=1" | 131 | url += ";subpath=%s" % paths[module] |
121 | 132 | ||
122 | ld = d.createCopy() | 133 | ld = d.createCopy() |
123 | # Not necessary to set SRC_URI, since we're passing the URI to | 134 | # Not necessary to set SRC_URI, since we're passing the URI to |
124 | # Fetch. | 135 | # Fetch. |
125 | #ld.setVar('SRC_URI', url) | 136 | #ld.setVar('SRC_URI', url) |
126 | ld.setVar('SRCREV_%s' % module, module_hash) | 137 | ld.setVar('SRCREV_%s' % module, subrevision[module]) |
127 | 138 | ||
128 | # Workaround for issues with SRCPV/SRCREV_FORMAT errors | 139 | # Workaround for issues with SRCPV/SRCREV_FORMAT errors |
129 | # error refer to 'multiple' repositories. Only the repository | 140 | # error refer to 'multiple' repositories. Only the repository |
@@ -131,125 +142,58 @@ class GitSM(Git): | |||
131 | ld.setVar('SRCPV', d.getVar('SRCPV')) | 142 | ld.setVar('SRCPV', d.getVar('SRCPV')) |
132 | ld.setVar('SRCREV_FORMAT', module) | 143 | ld.setVar('SRCREV_FORMAT', module) |
133 | 144 | ||
134 | newfetch = Fetch([url], ld, cache=False) | 145 | function(ud, url, module, paths[module], ld) |
135 | newfetch.download() | ||
136 | local_paths[module] = newfetch.localpath(url) | ||
137 | 146 | ||
138 | # Correct the submodule references to the local download version... | 147 | return submodules != [] |
139 | runfetchcmd("%(basecmd)s config submodule.%(module)s.url %(url)s" % {'basecmd': ud.basecmd, 'module': module, 'url' : local_paths[module]}, d, workdir=ud.clonedir) | ||
140 | |||
141 | symlink_path = os.path.join(ud.clonedir, 'modules', paths[module]) | ||
142 | if not os.path.exists(symlink_path): | ||
143 | try: | ||
144 | os.makedirs(os.path.dirname(symlink_path), exist_ok=True) | ||
145 | except OSError: | ||
146 | pass | ||
147 | os.symlink(local_paths[module], symlink_path) | ||
148 | |||
149 | return True | ||
150 | 148 | ||
151 | def download(self, ud, d): | 149 | def download(self, ud, d): |
152 | Git.download(self, ud, d) | 150 | def download_submodule(ud, url, module, modpath, d): |
153 | self.update_submodules(ud, d) | 151 | url += ";bareclone=1;nobranch=1" |
154 | |||
155 | def unpack_submodules(self, repo_conf, ud, d): | ||
156 | submodules = [] | ||
157 | paths = {} | ||
158 | revision = {} | ||
159 | uris = {} | ||
160 | local_paths = {} | ||
161 | |||
162 | for name in ud.names: | ||
163 | try: | ||
164 | gitmodules = runfetchcmd("%s show %s:.gitmodules" % (ud.basecmd, ud.revisions[name]), d, quiet=True, workdir=ud.destdir) | ||
165 | except: | ||
166 | # No submodules to update | ||
167 | continue | ||
168 | |||
169 | for m, md in self.parse_gitmodules(gitmodules).items(): | ||
170 | submodules.append(m) | ||
171 | paths[m] = md['path'] | ||
172 | revision[m] = ud.revisions[name] | ||
173 | uris[m] = md['url'] | ||
174 | if uris[m].startswith('..'): | ||
175 | newud = copy.copy(ud) | ||
176 | newud.path = os.path.realpath(os.path.join(newud.path, md['url'])) | ||
177 | uris[m] = Git._get_repo_url(self, newud) | ||
178 | 152 | ||
179 | modules_updated = False | 153 | # Is the following still needed? |
154 | #url += ";nocheckout=1" | ||
180 | 155 | ||
181 | for module in submodules: | ||
182 | try: | 156 | try: |
183 | module_hash = runfetchcmd("%s ls-tree -z -d %s %s" % (ud.basecmd, revision[module], paths[module]), d, quiet=True, workdir=ud.destdir) | 157 | newfetch = Fetch([url], d, cache=False) |
184 | except: | 158 | newfetch.download() |
185 | # If the command fails, we don't have a valid file to check. If it doesn't | 159 | except Exception as e: |
186 | # fail -- it still might be a failure, see next check... | 160 | logger.error('gitsm: submodule download failed: %s %s' % (type(e).__name__, str(e))) |
187 | module_hash = "" | 161 | raise |
188 | 162 | ||
189 | if not module_hash: | 163 | Git.download(self, ud, d) |
190 | logger.debug(1, "submodule %s is defined, but is not initialized in the repository. Skipping", module) | 164 | self.process_submodules(ud, ud.clonedir, download_submodule, d) |
191 | continue | ||
192 | |||
193 | modules_updated = True | ||
194 | 165 | ||
195 | module_hash = module_hash.split()[2] | 166 | def unpack(self, ud, destdir, d): |
167 | def unpack_submodules(ud, url, module, modpath, d): | ||
168 | url += ";bareclone=1;nobranch=1" | ||
196 | 169 | ||
197 | # Build new SRC_URI | 170 | # Figure out where we clone over the bare submodules... |
198 | if "://" not in uris[module]: | 171 | if ud.bareclone: |
199 | # It's ssh if the format does NOT have "://", but has a ':' | 172 | repo_conf = ud.destdir |
200 | if ":" in uris[module]: | ||
201 | proto = "ssh" | ||
202 | if ":/" in uris[module]: | ||
203 | url = "gitsm://" + uris[module].replace(':/', '/', 1) | ||
204 | else: | ||
205 | url = "gitsm://" + uris[module].replace(':', '/', 1) | ||
206 | else: # Fall back to 'file' if there is no ':' | ||
207 | proto = "file" | ||
208 | url = "gitsm://" + uris[module] | ||
209 | else: | 173 | else: |
210 | proto = uris[module].split(':', 1)[0] | 174 | repo_conf = os.path.join(ud.destdir, '.git') |
211 | url = uris[module].replace('%s:' % proto, 'gitsm:', 1) | ||
212 | |||
213 | url += ';protocol=%s' % proto | ||
214 | url += ";name=%s" % module | ||
215 | url += ";bareclone=1;nobranch=1;subpath=%s" % paths[module] | ||
216 | |||
217 | ld = d.createCopy() | ||
218 | # Not necessary to set SRC_URI, since we're passing the URI to | ||
219 | # Fetch. | ||
220 | #ld.setVar('SRC_URI', url) | ||
221 | ld.setVar('SRCREV_%s' % module, module_hash) | ||
222 | 175 | ||
223 | # Workaround for issues with SRCPV/SRCREV_FORMAT errors | 176 | try: |
224 | # error refer to 'multiple' repositories. Only the repository | 177 | newfetch = Fetch([url], d, cache=False) |
225 | # in the original SRC_URI actually matters... | 178 | newfetch.unpack(root=os.path.join(repo_conf, 'modules')) |
226 | ld.setVar('SRCPV', d.getVar('SRCPV')) | 179 | except Exception as e: |
227 | ld.setVar('SRCREV_FORMAT', module) | 180 | logger.error('gitsm: submodule unpack failed: %s %s' % (type(e).__name__, str(e))) |
181 | raise | ||
228 | 182 | ||
229 | newfetch = Fetch([url], ld, cache=False) | 183 | newfetch = Fetch([url], d, cache=False) |
230 | newfetch.unpack(root=os.path.join(repo_conf, 'modules')) | 184 | local_path = newfetch.localpath(url) |
231 | local_paths[module] = newfetch.localpath(url) | ||
232 | 185 | ||
233 | # Correct the submodule references to the local download version... | 186 | # Correct the submodule references to the local download version... |
234 | runfetchcmd("%(basecmd)s config submodule.%(module)s.url %(url)s" % {'basecmd': ud.basecmd, 'module': module, 'url' : local_paths[module]}, d, workdir=ud.destdir) | 187 | runfetchcmd("%(basecmd)s config submodule.%(module)s.url %(url)s" % {'basecmd': ud.basecmd, 'module': module, 'url' : local_path}, d, workdir=ud.destdir) |
235 | 188 | ||
236 | if ud.shallow: | 189 | if ud.shallow: |
237 | runfetchcmd("%(basecmd)s config submodule.%(module)s.shallow true" % {'basecmd': ud.basecmd, 'module': module}, d, workdir=ud.destdir) | 190 | runfetchcmd("%(basecmd)s config submodule.%(module)s.shallow true" % {'basecmd': ud.basecmd, 'module': module}, d, workdir=ud.destdir) |
238 | 191 | ||
239 | # Ensure the submodule repository is NOT set to bare, since we're checking it out... | 192 | # Ensure the submodule repository is NOT set to bare, since we're checking it out... |
240 | runfetchcmd("%s config core.bare false" % (ud.basecmd), d, quiet=True, workdir=os.path.join(repo_conf, 'modules', paths[module])) | 193 | runfetchcmd("%s config core.bare false" % (ud.basecmd), d, quiet=True, workdir=os.path.join(repo_conf, 'modules', modpath)) |
241 | |||
242 | return modules_updated | ||
243 | 194 | ||
244 | def unpack(self, ud, destdir, d): | ||
245 | Git.unpack(self, ud, destdir, d) | 195 | Git.unpack(self, ud, destdir, d) |
246 | 196 | ||
247 | # Copy over the submodules' fetched histories too. | 197 | if not ud.bareclone and self.process_submodules(ud, ud.destdir, unpack_submodules, d): |
248 | if ud.bareclone: | ||
249 | repo_conf = ud.destdir | ||
250 | else: | ||
251 | repo_conf = os.path.join(ud.destdir, '.git') | ||
252 | |||
253 | if self.unpack_submodules(repo_conf, ud, d): | ||
254 | # Run submodule update, this sets up the directories -- without touching the config | 198 | # Run submodule update, this sets up the directories -- without touching the config |
255 | runfetchcmd("%s submodule update --recursive --no-fetch" % (ud.basecmd), d, quiet=True, workdir=ud.destdir) | 199 | runfetchcmd("%s submodule update --recursive --no-fetch" % (ud.basecmd), d, quiet=True, workdir=ud.destdir) |
diff --git a/bitbake/lib/bb/tests/fetch.py b/bitbake/lib/bb/tests/fetch.py index 5fb5d04cb0..1497a3cff7 100644 --- a/bitbake/lib/bb/tests/fetch.py +++ b/bitbake/lib/bb/tests/fetch.py | |||
@@ -894,15 +894,23 @@ class FetcherNetworkTest(FetcherTest): | |||
894 | @skipIfNoNetwork() | 894 | @skipIfNoNetwork() |
895 | def test_git_submodule(self): | 895 | def test_git_submodule(self): |
896 | # URL with ssh submodules | 896 | # URL with ssh submodules |
897 | url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=ssh-gitsm-tests;rev=0d3ffc14bce95e8b3a21a0a67bfe4c4a96ba6350" | 897 | url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=ssh-gitsm-tests;rev=f53765f515e0eeca569ed385bb1c89ce008bb058" |
898 | # Original URL (comment this if you have ssh access to git.yoctoproject.org) | 898 | # Original URL (comment this if you have ssh access to git.yoctoproject.org) |
899 | url = "gitsm://git.yoctoproject.org/git-submodule-test;rev=f12e57f2edf0aa534cf1616fa983d165a92b0842" | 899 | url = "gitsm://git.yoctoproject.org/git-submodule-test;branch=master;rev=132fea6e4dee56b61bcf5721c94e8b2445c6a017" |
900 | fetcher = bb.fetch.Fetch([url], self.d) | 900 | fetcher = bb.fetch.Fetch([url], self.d) |
901 | fetcher.download() | 901 | fetcher.download() |
902 | # Previous cwd has been deleted | 902 | # Previous cwd has been deleted |
903 | os.chdir(os.path.dirname(self.unpackdir)) | 903 | os.chdir(os.path.dirname(self.unpackdir)) |
904 | fetcher.unpack(self.unpackdir) | 904 | fetcher.unpack(self.unpackdir) |
905 | 905 | ||
906 | repo_path = os.path.join(self.tempdir, 'unpacked', 'git') | ||
907 | self.assertTrue(os.path.exists(repo_path), msg='Unpacked repository missing') | ||
908 | self.assertTrue(os.path.exists(os.path.join(repo_path, 'bitbake')), msg='bitbake submodule missing') | ||
909 | self.assertFalse(os.path.exists(os.path.join(repo_path, 'na')), msg='uninitialized submodule present') | ||
910 | |||
911 | # Only when we're running the extended test with a submodule's submodule, can we check this. | ||
912 | if os.path.exists(os.path.join(repo_path, 'bitbake-gitsm-test1')): | ||
913 | self.assertTrue(os.path.exists(os.path.join(repo_path, 'bitbake-gitsm-test1', 'bitbake')), msg='submodule of submodule missing') | ||
906 | 914 | ||
907 | class TrustedNetworksTest(FetcherTest): | 915 | class TrustedNetworksTest(FetcherTest): |
908 | def test_trusted_network(self): | 916 | def test_trusted_network(self): |