diff options
| author | Ross Burton <ross.burton@arm.com> | 2025-06-27 14:48:48 +0100 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2025-07-01 08:49:37 +0100 |
| commit | 43434a79c0ac8b0e7a7a5da8730b03af0dc17fd5 (patch) | |
| tree | 53416e7e4eccf7be3782e9c7bb6fd392d1c4b4e9 /scripts/lib | |
| parent | 9291f67f1e01fe58dab7c28cbc2bd443da7950dc (diff) | |
| download | poky-43434a79c0ac8b0e7a7a5da8730b03af0dc17fd5.tar.gz | |
recipetool/create_go: proxy module fetching to go-mod-update-modules
Now that the go-mod-update-modules class exists, this Go handler can
create a stub recipe and then proxy the module handling to the class.
(From OE-Core rev: 0aa406d0582d32399c48dfa78f24adc75696112c)
Signed-off-by: Ross Burton <ross.burton@arm.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts/lib')
| -rw-r--r-- | scripts/lib/recipetool/create_go.py | 152 |
1 files changed, 36 insertions, 116 deletions
diff --git a/scripts/lib/recipetool/create_go.py b/scripts/lib/recipetool/create_go.py index 3e9fc85784..4b1fa39d13 100644 --- a/scripts/lib/recipetool/create_go.py +++ b/scripts/lib/recipetool/create_go.py | |||
| @@ -11,17 +11,15 @@ | |||
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | from recipetool.create import RecipeHandler, handle_license_vars | 13 | from recipetool.create import RecipeHandler, handle_license_vars |
| 14 | from recipetool.create import find_licenses | ||
| 15 | 14 | ||
| 16 | import bb.utils | 15 | import bb.utils |
| 17 | import json | 16 | import json |
| 18 | import logging | 17 | import logging |
| 19 | import os | 18 | import os |
| 20 | import re | 19 | import re |
| 20 | import subprocess | ||
| 21 | import sys | 21 | import sys |
| 22 | import tempfile | 22 | import tempfile |
| 23 | import urllib.parse | ||
| 24 | import urllib.request | ||
| 25 | 23 | ||
| 26 | 24 | ||
| 27 | logger = logging.getLogger('recipetool') | 25 | logger = logging.getLogger('recipetool') |
| @@ -66,97 +64,6 @@ class GoRecipeHandler(RecipeHandler): | |||
| 66 | 64 | ||
| 67 | return bindir | 65 | return bindir |
| 68 | 66 | ||
| 69 | @staticmethod | ||
| 70 | def __unescape_path(path): | ||
| 71 | """Unescape capital letters using exclamation points.""" | ||
| 72 | return re.sub(r'!([a-z])', lambda m: m.group(1).upper(), path) | ||
| 73 | |||
| 74 | @staticmethod | ||
| 75 | def __fold_uri(uri): | ||
| 76 | """Fold URI for sorting shorter module paths before longer.""" | ||
| 77 | return uri.replace(';', ' ').replace('/', '!') | ||
| 78 | |||
| 79 | @staticmethod | ||
| 80 | def __go_run_cmd(cmd, cwd, d): | ||
| 81 | env = dict(os.environ, PATH=d.getVar('PATH'), GOMODCACHE=d.getVar('GOMODCACHE')) | ||
| 82 | return bb.process.run(cmd, env=env, shell=True, cwd=cwd) | ||
| 83 | |||
| 84 | def __go_mod(self, go_mod, srctree, localfilesdir, extravalues, d): | ||
| 85 | moddir = d.getVar('GOMODCACHE') | ||
| 86 | |||
| 87 | # List main packages and their dependencies with the go list command. | ||
| 88 | stdout, _ = self.__go_run_cmd(f"go list -json=Dir,Module -deps {go_mod['Module']['Path']}/...", srctree, d) | ||
| 89 | pkgs = json.loads('[' + stdout.replace('}\n{', '},\n{') + ']') | ||
| 90 | |||
| 91 | # Collect licenses for the dependencies. | ||
| 92 | licenses = set() | ||
| 93 | lic_files_chksum = [] | ||
| 94 | lic_files = {} | ||
| 95 | for pkg in pkgs: | ||
| 96 | # TODO: If the package is in a subdirectory with its own license | ||
| 97 | # files then report those istead of the license files found in the | ||
| 98 | # module root directory. | ||
| 99 | mod = pkg.get('Module', None) | ||
| 100 | if not mod or mod.get('Main', False): | ||
| 101 | continue | ||
| 102 | path = os.path.relpath(mod['Dir'], moddir) | ||
| 103 | for lic in find_licenses(mod['Dir'], d): | ||
| 104 | lic_files[os.path.join(path, lic[1])] = (lic[0], lic[2]) | ||
| 105 | |||
| 106 | for lic_file in lic_files: | ||
| 107 | licenses.add(lic_files[lic_file][0]) | ||
| 108 | lic_files_chksum.append( | ||
| 109 | f'file://pkg/mod/{lic_file};md5={lic_files[lic_file][1]}') | ||
| 110 | |||
| 111 | # Collect the module cache files downloaded by the go list command as | ||
| 112 | # the go list command knows best what the go list command needs and it | ||
| 113 | # needs more files in the module cache than the go install command as | ||
| 114 | # it doesn't do the dependency pruning mentioned in the Go module | ||
| 115 | # reference, https://go.dev/ref/mod, for go 1.17 or higher. | ||
| 116 | src_uris = [] | ||
| 117 | downloaddir = os.path.join(moddir, 'cache', 'download') | ||
| 118 | for dirpath, _, filenames in os.walk(downloaddir): | ||
| 119 | path, base = os.path.split(os.path.relpath(dirpath, downloaddir)) | ||
| 120 | if base != '@v': | ||
| 121 | continue | ||
| 122 | path = self.__unescape_path(path) | ||
| 123 | zipver = None | ||
| 124 | for name in filenames: | ||
| 125 | ver, ext = os.path.splitext(name) | ||
| 126 | if ext == '.zip': | ||
| 127 | chksum = bb.utils.sha256_file(os.path.join(dirpath, name)) | ||
| 128 | src_uris.append(f'gomod://{path};version={ver};sha256sum={chksum}') | ||
| 129 | zipver = ver | ||
| 130 | break | ||
| 131 | for name in filenames: | ||
| 132 | ver, ext = os.path.splitext(name) | ||
| 133 | if ext == '.mod' and ver != zipver: | ||
| 134 | chksum = bb.utils.sha256_file(os.path.join(dirpath, name)) | ||
| 135 | src_uris.append(f'gomod://{path};version={ver};mod=1;sha256sum={chksum}') | ||
| 136 | |||
| 137 | self.__go_run_cmd("go clean -modcache", srctree, d) | ||
| 138 | |||
| 139 | licenses_basename = "{pn}-licenses.inc" | ||
| 140 | licenses_filename = os.path.join(localfilesdir, licenses_basename) | ||
| 141 | with open(licenses_filename, "w") as f: | ||
| 142 | f.write(f'GO_MOD_LICENSES = "{" & ".join(sorted(licenses))}"\n\n') | ||
| 143 | f.write('LIC_FILES_CHKSUM += "\\\n') | ||
| 144 | for lic in sorted(lic_files_chksum, key=self.__fold_uri): | ||
| 145 | f.write(' ' + lic + ' \\\n') | ||
| 146 | f.write('"\n') | ||
| 147 | |||
| 148 | extravalues['extrafiles'][f"../{licenses_basename}"] = licenses_filename | ||
| 149 | |||
| 150 | go_mods_basename = "{pn}-go-mods.inc" | ||
| 151 | go_mods_filename = os.path.join(localfilesdir, go_mods_basename) | ||
| 152 | with open(go_mods_filename, "w") as f: | ||
| 153 | f.write('SRC_URI += "\\\n') | ||
| 154 | for uri in sorted(src_uris, key=self.__fold_uri): | ||
| 155 | f.write(' ' + uri + ' \\\n') | ||
| 156 | f.write('"\n') | ||
| 157 | |||
| 158 | extravalues['extrafiles'][f"../{go_mods_basename}"] = go_mods_filename | ||
| 159 | |||
| 160 | def process(self, srctree, classes, lines_before, | 67 | def process(self, srctree, classes, lines_before, |
| 161 | lines_after, handled, extravalues): | 68 | lines_after, handled, extravalues): |
| 162 | 69 | ||
| @@ -167,37 +74,52 @@ class GoRecipeHandler(RecipeHandler): | |||
| 167 | if not files: | 74 | if not files: |
| 168 | return False | 75 | return False |
| 169 | 76 | ||
| 170 | d = bb.data.createCopy(tinfoil.config_data) | ||
| 171 | go_bindir = self.__ensure_go() | 77 | go_bindir = self.__ensure_go() |
| 172 | if not go_bindir: | 78 | if not go_bindir: |
| 173 | sys.exit(14) | 79 | sys.exit(14) |
| 174 | 80 | ||
| 175 | d.prependVar('PATH', '%s:' % go_bindir) | ||
| 176 | handled.append('buildsystem') | 81 | handled.append('buildsystem') |
| 177 | classes.append("go-mod") | 82 | classes.append("go-mod") |
| 178 | 83 | ||
| 179 | tmp_mod_dir = tempfile.mkdtemp(prefix='go-mod-') | 84 | # Use go-mod-update-modules to set the full SRC_URI and LICENSE |
| 180 | d.setVar('GOMODCACHE', tmp_mod_dir) | 85 | classes.append("go-mod-update-modules") |
| 86 | extravalues["run_tasks"] = "update_modules" | ||
| 87 | |||
| 88 | with tempfile.TemporaryDirectory(prefix="go-mod-") as tmp_mod_dir: | ||
| 89 | env = dict(os.environ) | ||
| 90 | env["PATH"] += f":{go_bindir}" | ||
| 91 | env['GOMODCACHE'] = tmp_mod_dir | ||
| 181 | 92 | ||
| 182 | stdout, _ = self.__go_run_cmd("go mod edit -json", srctree, d) | 93 | stdout = subprocess.check_output(["go", "mod", "edit", "-json"], cwd=srctree, env=env, text=True) |
| 183 | go_mod = json.loads(stdout) | 94 | go_mod = json.loads(stdout) |
| 184 | go_import = re.sub(r'/v([0-9]+)$', '', go_mod['Module']['Path']) | 95 | go_import = re.sub(r'/v([0-9]+)$', '', go_mod['Module']['Path']) |
| 185 | 96 | ||
| 186 | localfilesdir = tempfile.mkdtemp(prefix='recipetool-go-') | 97 | localfilesdir = tempfile.mkdtemp(prefix='recipetool-go-') |
| 187 | extravalues.setdefault('extrafiles', {}) | 98 | extravalues.setdefault('extrafiles', {}) |
| 188 | 99 | ||
| 189 | # Write the ${BPN}-licenses.inc and ${BPN}-go-mods.inc files | 100 | # Write the stub ${BPN}-licenses.inc and ${BPN}-go-mods.inc files |
| 190 | self.__go_mod(go_mod, srctree, localfilesdir, extravalues, d) | 101 | basename = "{pn}-licenses.inc" |
| 102 | filename = os.path.join(localfilesdir, basename) | ||
| 103 | with open(filename, "w") as f: | ||
| 104 | f.write("# FROM RECIPETOOL\n") | ||
| 105 | extravalues['extrafiles'][f"../{basename}"] = filename | ||
| 191 | 106 | ||
| 192 | # Do generic license handling | 107 | basename = "{pn}-go-mods.inc" |
| 193 | handle_license_vars(srctree, lines_before, handled, extravalues, d) | 108 | filename = os.path.join(localfilesdir, basename) |
| 194 | self.__rewrite_lic_vars(lines_before) | 109 | with open(filename, "w") as f: |
| 110 | f.write("# FROM RECIPETOOL\n") | ||
| 111 | extravalues['extrafiles'][f"../{basename}"] = filename | ||
| 195 | 112 | ||
| 196 | self.__rewrite_src_uri(lines_before) | 113 | # Do generic license handling |
| 114 | d = bb.data.createCopy(tinfoil.config_data) | ||
| 115 | handle_license_vars(srctree, lines_before, handled, extravalues, d) | ||
| 116 | self.__rewrite_lic_vars(lines_before) | ||
| 197 | 117 | ||
| 198 | lines_before.append('require ${BPN}-licenses.inc') | 118 | self.__rewrite_src_uri(lines_before) |
| 199 | lines_before.append('require ${BPN}-go-mods.inc') | 119 | |
| 200 | lines_before.append(f'GO_IMPORT = "{go_import}"') | 120 | lines_before.append('require ${BPN}-licenses.inc') |
| 121 | lines_before.append('require ${BPN}-go-mods.inc') | ||
| 122 | lines_before.append(f'GO_IMPORT = "{go_import}"') | ||
| 201 | 123 | ||
| 202 | def __update_lines_before(self, updated, newlines, lines_before): | 124 | def __update_lines_before(self, updated, newlines, lines_before): |
| 203 | if updated: | 125 | if updated: |
| @@ -210,10 +132,8 @@ class GoRecipeHandler(RecipeHandler): | |||
| 210 | return updated | 132 | return updated |
| 211 | 133 | ||
| 212 | def __rewrite_lic_vars(self, lines_before): | 134 | def __rewrite_lic_vars(self, lines_before): |
| 213 | |||
| 214 | def varfunc(varname, origvalue, op, newlines): | 135 | def varfunc(varname, origvalue, op, newlines): |
| 215 | if varname == 'LICENSE': | 136 | import urllib.parse |
| 216 | return ' & '.join((origvalue, '${GO_MOD_LICENSES}')), None, -1, True | ||
| 217 | if varname == 'LIC_FILES_CHKSUM': | 137 | if varname == 'LIC_FILES_CHKSUM': |
| 218 | new_licenses = [] | 138 | new_licenses = [] |
| 219 | licenses = origvalue.split('\\') | 139 | licenses = origvalue.split('\\') |
| @@ -235,7 +155,7 @@ class GoRecipeHandler(RecipeHandler): | |||
| 235 | return origvalue, None, 0, True | 155 | return origvalue, None, 0, True |
| 236 | 156 | ||
| 237 | updated, newlines = bb.utils.edit_metadata( | 157 | updated, newlines = bb.utils.edit_metadata( |
| 238 | lines_before, ['LICENSE', 'LIC_FILES_CHKSUM'], varfunc) | 158 | lines_before, ['LIC_FILES_CHKSUM'], varfunc) |
| 239 | return self.__update_lines_before(updated, newlines, lines_before) | 159 | return self.__update_lines_before(updated, newlines, lines_before) |
| 240 | 160 | ||
| 241 | def __rewrite_src_uri(self, lines_before): | 161 | def __rewrite_src_uri(self, lines_before): |
