mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 12:59:02 +02:00

Build the 'downloadfilename' parameter by replacing path separators in the module path like the git fetcher builds the mirror tar ball name. Copy the downloaded file in the fetcher's unpack method like the crate fetcher instead of calling the base fetcher's unpack method. (Bitbake rev: 7762cea087597019460d66b04268757bd46befdf) Signed-off-by: Christian Lindeberg <christian.lindeberg@axis.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
274 lines
9.8 KiB
Python
274 lines
9.8 KiB
Python
"""
|
|
BitBake 'Fetch' implementation for Go modules
|
|
|
|
The gomod/gomodgit fetchers are used to download Go modules to the module cache
|
|
from a module proxy or directly from a version control repository.
|
|
|
|
Example SRC_URI:
|
|
|
|
SRC_URI += "gomod://golang.org/x/net;version=v0.9.0;sha256sum=..."
|
|
SRC_URI += "gomodgit://golang.org/x/net;version=v0.9.0;repo=go.googlesource.com/net;srcrev=..."
|
|
|
|
Required SRC_URI parameters:
|
|
|
|
- version
|
|
The version of the module.
|
|
|
|
Optional SRC_URI parameters:
|
|
|
|
- mod
|
|
Fetch and unpack the go.mod file only instead of the complete module.
|
|
The go command may need to download go.mod files for many different modules
|
|
when computing the build list, and go.mod files are much smaller than
|
|
module zip files.
|
|
The default is "0", set mod=1 for the go.mod file only.
|
|
|
|
- sha256sum
|
|
The checksum of the module zip file, or the go.mod file in case of fetching
|
|
only the go.mod file. Alternatively, set the SRC_URI varible flag for
|
|
"module@version.sha256sum".
|
|
|
|
- protocol
|
|
The method used when fetching directly from a version control repository.
|
|
The default is "https" for git.
|
|
|
|
- repo
|
|
The URL when fetching directly from a version control repository. Required
|
|
when the URL is different from the module path.
|
|
|
|
- srcrev
|
|
The revision identifier used when fetching directly from a version control
|
|
repository. Alternatively, set the SRCREV varible for "module@version".
|
|
|
|
- subdir
|
|
The module subdirectory when fetching directly from a version control
|
|
repository. Required when the module is not located in the root of the
|
|
repository.
|
|
|
|
Related variables:
|
|
|
|
- GO_MOD_PROXY
|
|
The module proxy used by the fetcher.
|
|
|
|
- GO_MOD_CACHE_DIR
|
|
The directory where the module cache is located.
|
|
This must match the exported GOMODCACHE variable for the go command to find
|
|
the downloaded modules.
|
|
|
|
See the Go modules reference, https://go.dev/ref/mod, for more information
|
|
about the module cache, module proxies and version control systems.
|
|
"""
|
|
|
|
import hashlib
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import zipfile
|
|
|
|
import bb
|
|
from bb.fetch2 import FetchError
|
|
from bb.fetch2 import MissingParameterError
|
|
from bb.fetch2 import runfetchcmd
|
|
from bb.fetch2 import subprocess_setup
|
|
from bb.fetch2.git import Git
|
|
from bb.fetch2.wget import Wget
|
|
|
|
|
|
def escape(path):
|
|
"""Escape capital letters using exclamation points."""
|
|
return re.sub(r'([A-Z])', lambda m: '!' + m.group(1).lower(), path)
|
|
|
|
|
|
class GoMod(Wget):
|
|
"""Class to fetch Go modules from a Go module proxy via wget"""
|
|
|
|
def supports(self, ud, d):
|
|
"""Check to see if a given URL is for this fetcher."""
|
|
return ud.type == 'gomod'
|
|
|
|
def urldata_init(self, ud, d):
|
|
"""Set up to download the module from the module proxy.
|
|
|
|
Set up to download the module zip file to the module cache directory
|
|
and unpack the go.mod file (unless downloading only the go.mod file):
|
|
|
|
cache/download/<module>/@v/<version>.zip: The module zip file.
|
|
cache/download/<module>/@v/<version>.mod: The go.mod file.
|
|
"""
|
|
|
|
proxy = d.getVar('GO_MOD_PROXY') or 'proxy.golang.org'
|
|
moddir = d.getVar('GO_MOD_CACHE_DIR') or 'pkg/mod'
|
|
|
|
if 'version' not in ud.parm:
|
|
raise MissingParameterError('version', ud.url)
|
|
|
|
module = ud.host
|
|
if ud.path != '/':
|
|
module += ud.path
|
|
ud.parm['module'] = module
|
|
version = ud.parm['version']
|
|
|
|
# Set URL and filename for wget download
|
|
if ud.parm.get('mod', '0') == '1':
|
|
ext = '.mod'
|
|
else:
|
|
ext = '.zip'
|
|
path = escape(f"{module}/@v/{version}{ext}")
|
|
ud.url = bb.fetch2.encodeurl(
|
|
('https', proxy, '/' + path, None, None, None))
|
|
ud.parm['downloadfilename'] = f"{module.replace('/', '.')}@{version}{ext}"
|
|
|
|
# Set name for checksum verification
|
|
ud.parm['name'] = f"{module}@{version}"
|
|
|
|
# Set path for unpack
|
|
ud.parm['unpackpath'] = os.path.join(moddir, 'cache/download', path)
|
|
|
|
super().urldata_init(ud, d)
|
|
|
|
def unpack(self, ud, rootdir, d):
|
|
"""Unpack the module in the module cache."""
|
|
|
|
# Unpack the module zip file or go.mod file
|
|
unpackpath = os.path.join(rootdir, ud.parm['unpackpath'])
|
|
unpackdir = os.path.dirname(unpackpath)
|
|
bb.utils.mkdirhier(unpackdir)
|
|
ud.unpack_tracer.unpack("file-copy", unpackdir)
|
|
cmd = f"cp {ud.localpath} {unpackpath}"
|
|
path = d.getVar('PATH')
|
|
if path:
|
|
cmd = f"PATH={path} {cmd}"
|
|
name = os.path.basename(unpackpath)
|
|
bb.note(f"Unpacking {name} to {unpackdir}/")
|
|
subprocess.check_call(cmd, shell=True, preexec_fn=subprocess_setup)
|
|
|
|
if name.endswith('.zip'):
|
|
# Unpack the go.mod file from the zip file
|
|
module = ud.parm['module']
|
|
name = name.rsplit('.', 1)[0] + '.mod'
|
|
bb.note(f"Unpacking {name} to {unpackdir}/")
|
|
with zipfile.ZipFile(ud.localpath) as zf:
|
|
with open(os.path.join(unpackdir, name), mode='wb') as mf:
|
|
try:
|
|
f = module + '@' + ud.parm['version'] + '/go.mod'
|
|
shutil.copyfileobj(zf.open(f), mf)
|
|
except KeyError:
|
|
# If the module does not have a go.mod file, synthesize
|
|
# one containing only a module statement.
|
|
mf.write(f'module {module}\n'.encode())
|
|
|
|
|
|
class GoModGit(Git):
|
|
"""Class to fetch Go modules directly from a git repository"""
|
|
|
|
def supports(self, ud, d):
|
|
"""Check to see if a given URL is for this fetcher."""
|
|
return ud.type == 'gomodgit'
|
|
|
|
def urldata_init(self, ud, d):
|
|
"""Set up to download the module from the git repository.
|
|
|
|
Set up to download the git repository to the module cache directory and
|
|
unpack the module zip file and the go.mod file:
|
|
|
|
cache/vcs/<hash>: The bare git repository.
|
|
cache/download/<module>/@v/<version>.zip: The module zip file.
|
|
cache/download/<module>/@v/<version>.mod: The go.mod file.
|
|
"""
|
|
|
|
moddir = d.getVar('GO_MOD_CACHE_DIR') or 'pkg/mod'
|
|
|
|
if 'version' not in ud.parm:
|
|
raise MissingParameterError('version', ud.url)
|
|
|
|
module = ud.host
|
|
if ud.path != '/':
|
|
module += ud.path
|
|
ud.parm['module'] = module
|
|
|
|
# Set host, path and srcrev for git download
|
|
if 'repo' in ud.parm:
|
|
repo = ud.parm['repo']
|
|
idx = repo.find('/')
|
|
if idx != -1:
|
|
ud.host = repo[:idx]
|
|
ud.path = repo[idx:]
|
|
else:
|
|
ud.host = repo
|
|
ud.path = ''
|
|
if 'protocol' not in ud.parm:
|
|
ud.parm['protocol'] = 'https'
|
|
ud.name = f"{module}@{ud.parm['version']}"
|
|
srcrev = d.getVar('SRCREV_' + ud.name)
|
|
if srcrev:
|
|
if 'srcrev' not in ud.parm:
|
|
ud.parm['srcrev'] = srcrev
|
|
else:
|
|
if 'srcrev' in ud.parm:
|
|
d.setVar('SRCREV_' + ud.name, ud.parm['srcrev'])
|
|
if 'branch' not in ud.parm:
|
|
ud.parm['nobranch'] = '1'
|
|
|
|
# Set subpath, subdir and bareclone for git unpack
|
|
if 'subdir' in ud.parm:
|
|
ud.parm['subpath'] = ud.parm['subdir']
|
|
key = f"git3:{ud.parm['protocol']}://{ud.host}{ud.path}".encode()
|
|
ud.parm['key'] = key
|
|
ud.parm['subdir'] = os.path.join(moddir, 'cache/vcs',
|
|
hashlib.sha256(key).hexdigest())
|
|
ud.parm['bareclone'] = '1'
|
|
|
|
super().urldata_init(ud, d)
|
|
|
|
def unpack(self, ud, rootdir, d):
|
|
"""Unpack the module in the module cache."""
|
|
|
|
# Unpack the bare git repository
|
|
super().unpack(ud, rootdir, d)
|
|
|
|
moddir = d.getVar('GO_MOD_CACHE_DIR') or 'pkg/mod'
|
|
|
|
# Create the info file
|
|
module = ud.parm['module']
|
|
repodir = os.path.join(rootdir, ud.parm['subdir'])
|
|
with open(repodir + '.info', 'wb') as f:
|
|
f.write(ud.parm['key'])
|
|
|
|
# Unpack the go.mod file from the repository
|
|
unpackdir = os.path.join(rootdir, moddir, 'cache/download',
|
|
escape(module), '@v')
|
|
bb.utils.mkdirhier(unpackdir)
|
|
srcrev = ud.parm['srcrev']
|
|
version = ud.parm['version']
|
|
escaped_version = escape(version)
|
|
cmd = f"git ls-tree -r --name-only '{srcrev}'"
|
|
if 'subpath' in ud.parm:
|
|
cmd += f" '{ud.parm['subpath']}'"
|
|
files = runfetchcmd(cmd, d, workdir=repodir).split()
|
|
name = escaped_version + '.mod'
|
|
bb.note(f"Unpacking {name} to {unpackdir}/")
|
|
with open(os.path.join(unpackdir, name), mode='wb') as mf:
|
|
f = 'go.mod'
|
|
if 'subpath' in ud.parm:
|
|
f = os.path.join(ud.parm['subpath'], f)
|
|
if f in files:
|
|
cmd = ['git', 'cat-file', 'blob', srcrev + ':' + f]
|
|
subprocess.check_call(cmd, stdout=mf, cwd=repodir,
|
|
preexec_fn=subprocess_setup)
|
|
else:
|
|
# If the module does not have a go.mod file, synthesize one
|
|
# containing only a module statement.
|
|
mf.write(f'module {module}\n'.encode())
|
|
|
|
# Synthesize the module zip file from the repository
|
|
name = escaped_version + '.zip'
|
|
bb.note(f"Unpacking {name} to {unpackdir}/")
|
|
with zipfile.ZipFile(os.path.join(unpackdir, name), mode='w') as zf:
|
|
prefix = module + '@' + version + '/'
|
|
for f in files:
|
|
cmd = ['git', 'cat-file', 'blob', srcrev + ':' + f]
|
|
data = subprocess.check_output(cmd, cwd=repodir,
|
|
preexec_fn=subprocess_setup)
|
|
zf.writestr(prefix + f, data)
|