summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bitbake/lib/bb/fetch2/__init__.py4
-rw-r--r--bitbake/lib/bb/fetch2/gomod.py128
-rw-r--r--bitbake/lib/bb/tests/fetch.py65
3 files changed, 196 insertions, 1 deletions
diff --git a/bitbake/lib/bb/fetch2/__init__.py b/bitbake/lib/bb/fetch2/__init__.py
index 5bf2c4b8cf..f84ce59992 100644
--- a/bitbake/lib/bb/fetch2/__init__.py
+++ b/bitbake/lib/bb/fetch2/__init__.py
@@ -1317,7 +1317,7 @@ class FetchData(object):
1317 1317
1318 if checksum_name in self.parm: 1318 if checksum_name in self.parm:
1319 checksum_expected = self.parm[checksum_name] 1319 checksum_expected = self.parm[checksum_name]
1320 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3", "az", "crate", "gs"]: 1320 elif self.type not in ["http", "https", "ftp", "ftps", "sftp", "s3", "az", "crate", "gs", "gomod"]:
1321 checksum_expected = None 1321 checksum_expected = None
1322 else: 1322 else:
1323 checksum_expected = d.getVarFlag("SRC_URI", checksum_name) 1323 checksum_expected = d.getVarFlag("SRC_URI", checksum_name)
@@ -2088,6 +2088,7 @@ from . import npmsw
2088from . import az 2088from . import az
2089from . import crate 2089from . import crate
2090from . import gcp 2090from . import gcp
2091from . import gomod
2091 2092
2092methods.append(local.Local()) 2093methods.append(local.Local())
2093methods.append(wget.Wget()) 2094methods.append(wget.Wget())
@@ -2110,3 +2111,4 @@ methods.append(npmsw.NpmShrinkWrap())
2110methods.append(az.Az()) 2111methods.append(az.Az())
2111methods.append(crate.Crate()) 2112methods.append(crate.Crate())
2112methods.append(gcp.GCP()) 2113methods.append(gcp.GCP())
2114methods.append(gomod.GoMod())
diff --git a/bitbake/lib/bb/fetch2/gomod.py b/bitbake/lib/bb/fetch2/gomod.py
new file mode 100644
index 0000000000..fe025e367f
--- /dev/null
+++ b/bitbake/lib/bb/fetch2/gomod.py
@@ -0,0 +1,128 @@
1"""
2BitBake 'Fetch' implementation for Go modules
3
4The gomod fetcher is used to download Go modules to the module cache from a
5module proxy.
6
7Example SRC_URI:
8
9SRC_URI += "gomod://golang.org/x/net;version=v0.9.0;sha256sum=..."
10
11Required SRC_URI parameters:
12
13- version
14 The version of the module.
15
16Optional SRC_URI parameters:
17
18- mod
19 Fetch and unpack the go.mod file only instead of the complete module.
20 The go command may need to download go.mod files for many different modules
21 when computing the build list, and go.mod files are much smaller than
22 module zip files.
23 The default is "0", set mod=1 for the go.mod file only.
24
25- sha256sum
26 The checksum of the module zip file, or the go.mod file in case of fetching
27 only the go.mod file. Alternatively, set the SRC_URI varible flag for
28 "module@version.sha256sum".
29
30Related variables:
31
32- GO_MOD_PROXY
33 The module proxy used by the fetcher.
34
35- GO_MOD_CACHE_DIR
36 The directory where the module cache is located.
37 This must match the exported GOMODCACHE variable for the go command to find
38 the downloaded modules.
39
40See the Go modules reference, https://go.dev/ref/mod, for more information
41about the module cache, module proxies and version control systems.
42"""
43
44import os
45import re
46import shutil
47import zipfile
48
49import bb
50from bb.fetch2 import FetchError
51from bb.fetch2 import MissingParameterError
52from bb.fetch2.wget import Wget
53
54
55def escape(path):
56 """Escape capital letters using exclamation points."""
57 return re.sub(r'([A-Z])', lambda m: '!' + m.group(1).lower(), path)
58
59
60class GoMod(Wget):
61 """Class to fetch Go modules from a Go module proxy via wget"""
62
63 def supports(self, ud, d):
64 """Check to see if a given URL is for this fetcher."""
65 return ud.type == 'gomod'
66
67 def urldata_init(self, ud, d):
68 """Set up to download the module from the module proxy.
69
70 Set up to download the module zip file to the module cache directory
71 and unpack the go.mod file (unless downloading only the go.mod file):
72
73 cache/download/<module>/@v/<version>.zip: The module zip file.
74 cache/download/<module>/@v/<version>.mod: The go.mod file.
75 """
76
77 proxy = d.getVar('GO_MOD_PROXY') or 'proxy.golang.org'
78 moddir = d.getVar('GO_MOD_CACHE_DIR') or 'pkg/mod'
79
80 if 'version' not in ud.parm:
81 raise MissingParameterError('version', ud.url)
82
83 module = ud.host + ud.path
84 ud.parm['module'] = module
85
86 # Set URL and filename for wget download
87 path = escape(module + '/@v/' + ud.parm['version'])
88 if ud.parm.get('mod', '0') == '1':
89 path += '.mod'
90 else:
91 path += '.zip'
92 ud.parm['unpack'] = '0'
93 ud.url = bb.fetch2.encodeurl(
94 ('https', proxy, '/' + path, None, None, None))
95 ud.parm['downloadfilename'] = path
96
97 # Set name parameter if sha256sum is set in recipe
98 name = f"{module}@{ud.parm['version']}"
99 if d.getVarFlag('SRC_URI', name + '.sha256sum'):
100 ud.parm['name'] = name
101
102 # Set subdir for unpack
103 ud.parm['subdir'] = os.path.join(moddir, 'cache/download',
104 os.path.dirname(path))
105
106 super().urldata_init(ud, d)
107
108 def unpack(self, ud, rootdir, d):
109 """Unpack the module in the module cache."""
110
111 # Unpack the module zip file or go.mod file
112 super().unpack(ud, rootdir, d)
113
114 if ud.localpath.endswith('.zip'):
115 # Unpack the go.mod file from the zip file
116 module = ud.parm['module']
117 unpackdir = os.path.join(rootdir, ud.parm['subdir'])
118 name = os.path.basename(ud.localpath).rsplit('.', 1)[0] + '.mod'
119 bb.note(f"Unpacking {name} to {unpackdir}/")
120 with zipfile.ZipFile(ud.localpath) as zf:
121 with open(os.path.join(unpackdir, name), mode='wb') as mf:
122 try:
123 f = module + '@' + ud.parm['version'] + '/go.mod'
124 shutil.copyfileobj(zf.open(f), mf)
125 except KeyError:
126 # If the module does not have a go.mod file, synthesize
127 # one containing only a module statement.
128 mf.write(f'module {module}\n'.encode())
diff --git a/bitbake/lib/bb/tests/fetch.py b/bitbake/lib/bb/tests/fetch.py
index 2ef2063436..2365a50960 100644
--- a/bitbake/lib/bb/tests/fetch.py
+++ b/bitbake/lib/bb/tests/fetch.py
@@ -3390,3 +3390,68 @@ class FetchPremirroronlyBrokenTarball(FetcherTest):
3390 fetcher.download() 3390 fetcher.download()
3391 output = "".join(logs.output) 3391 output = "".join(logs.output)
3392 self.assertFalse(" not a git repository (or any parent up to mount point /)" in output) 3392 self.assertFalse(" not a git repository (or any parent up to mount point /)" in output)
3393
3394class GoModTest(FetcherTest):
3395
3396 @skipIfNoNetwork()
3397 def test_gomod_url(self):
3398 urls = ['gomod://github.com/Azure/azure-sdk-for-go/sdk/storage/azblob;version=v1.0.0;'
3399 'sha256sum=9bb69aea32f1d59711701f9562d66432c9c0374205e5009d1d1a62f03fb4fdad']
3400
3401 fetcher = bb.fetch2.Fetch(urls, self.d)
3402 ud = fetcher.ud[urls[0]]
3403 self.assertEqual(ud.url, 'https://proxy.golang.org/github.com/%21azure/azure-sdk-for-go/sdk/storage/azblob/%40v/v1.0.0.zip')
3404 self.assertNotIn('name', ud.parm)
3405
3406 fetcher.download()
3407 fetcher.unpack(self.unpackdir)
3408 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3409 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.zip')))
3410 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')))
3411
3412 @skipIfNoNetwork()
3413 def test_gomod_url_go_mod_only(self):
3414 urls = ['gomod://github.com/Azure/azure-sdk-for-go/sdk/storage/azblob;version=v1.0.0;mod=1;'
3415 'sha256sum=7873b8544842329b4f385a3aa6cf82cc2bc8defb41a04fa5291c35fd5900e873']
3416
3417 fetcher = bb.fetch2.Fetch(urls, self.d)
3418 ud = fetcher.ud[urls[0]]
3419 self.assertEqual(ud.url, 'https://proxy.golang.org/github.com/%21azure/azure-sdk-for-go/sdk/storage/azblob/%40v/v1.0.0.mod')
3420 self.assertNotIn('name', ud.parm)
3421
3422 fetcher.download()
3423 fetcher.unpack(self.unpackdir)
3424 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3425 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'github.com/!azure/azure-sdk-for-go/sdk/storage/azblob/@v/v1.0.0.mod')))
3426
3427 @skipIfNoNetwork()
3428 def test_gomod_url_sha256sum_varflag(self):
3429 urls = ['gomod://gopkg.in/ini.v1;version=v1.67.0']
3430 self.d.setVarFlag('SRC_URI', 'gopkg.in/ini.v1@v1.67.0.sha256sum', 'bd845dfc762a87a56e5a32a07770dc83e86976db7705d7f89c5dbafdc60b06c6')
3431
3432 fetcher = bb.fetch2.Fetch(urls, self.d)
3433 ud = fetcher.ud[urls[0]]
3434 self.assertEqual(ud.url, 'https://proxy.golang.org/gopkg.in/ini.v1/%40v/v1.67.0.zip')
3435 self.assertEqual(ud.parm['name'], 'gopkg.in/ini.v1@v1.67.0')
3436
3437 fetcher.download()
3438 fetcher.unpack(self.unpackdir)
3439 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3440 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.zip')))
3441 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')))
3442
3443 @skipIfNoNetwork()
3444 def test_gomod_url_no_go_mod_in_module(self):
3445 urls = ['gomod://gopkg.in/ini.v1;version=v1.67.0;'
3446 'sha256sum=bd845dfc762a87a56e5a32a07770dc83e86976db7705d7f89c5dbafdc60b06c6']
3447
3448 fetcher = bb.fetch2.Fetch(urls, self.d)
3449 ud = fetcher.ud[urls[0]]
3450 self.assertEqual(ud.url, 'https://proxy.golang.org/gopkg.in/ini.v1/%40v/v1.67.0.zip')
3451 self.assertNotIn('name', ud.parm)
3452
3453 fetcher.download()
3454 fetcher.unpack(self.unpackdir)
3455 downloaddir = os.path.join(self.unpackdir, 'pkg/mod/cache/download')
3456 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.zip')))
3457 self.assertTrue(os.path.exists(os.path.join(downloaddir, 'gopkg.in/ini.v1/@v/v1.67.0.mod')))