summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/fetch2/gomod.py
blob: fe025e367fa91471833763d5259b3d776644e843 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""
BitBake 'Fetch' implementation for Go modules

The gomod fetcher is used to download Go modules to the module cache from a
module proxy.

Example SRC_URI:

SRC_URI += "gomod://golang.org/x/net;version=v0.9.0;sha256sum=..."

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".

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 os
import re
import shutil
import zipfile

import bb
from bb.fetch2 import FetchError
from bb.fetch2 import MissingParameterError
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 + ud.path
        ud.parm['module'] = module

        # Set URL and filename for wget download
        path = escape(module + '/@v/' + ud.parm['version'])
        if ud.parm.get('mod', '0') == '1':
            path += '.mod'
        else:
            path += '.zip'
            ud.parm['unpack'] = '0'
        ud.url = bb.fetch2.encodeurl(
            ('https', proxy, '/' + path, None, None, None))
        ud.parm['downloadfilename'] = path

        # Set name parameter if sha256sum is set in recipe
        name = f"{module}@{ud.parm['version']}"
        if d.getVarFlag('SRC_URI', name + '.sha256sum'):
            ud.parm['name'] = name

        # Set subdir for unpack
        ud.parm['subdir'] = os.path.join(moddir, 'cache/download',
                                         os.path.dirname(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
        super().unpack(ud, rootdir, d)

        if ud.localpath.endswith('.zip'):
            # Unpack the go.mod file from the zip file
            module = ud.parm['module']
            unpackdir = os.path.join(rootdir, ud.parm['subdir'])
            name = os.path.basename(ud.localpath).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())