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): |