diff options
Diffstat (limited to 'scripts/lib/recipetool/create_go.py')
-rw-r--r-- | scripts/lib/recipetool/create_go.py | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/scripts/lib/recipetool/create_go.py b/scripts/lib/recipetool/create_go.py new file mode 100644 index 0000000000..4b1fa39d13 --- /dev/null +++ b/scripts/lib/recipetool/create_go.py | |||
@@ -0,0 +1,174 @@ | |||
1 | # Recipe creation tool - go support plugin | ||
2 | # | ||
3 | # The code is based on golang internals. See the afftected | ||
4 | # methods for further reference and information. | ||
5 | # | ||
6 | # Copyright (C) 2023 Weidmueller GmbH & Co KG | ||
7 | # Author: Lukas Funke <lukas.funke@weidmueller.com> | ||
8 | # | ||
9 | # SPDX-License-Identifier: GPL-2.0-only | ||
10 | # | ||
11 | |||
12 | |||
13 | from recipetool.create import RecipeHandler, handle_license_vars | ||
14 | |||
15 | import bb.utils | ||
16 | import json | ||
17 | import logging | ||
18 | import os | ||
19 | import re | ||
20 | import subprocess | ||
21 | import sys | ||
22 | import tempfile | ||
23 | |||
24 | |||
25 | logger = logging.getLogger('recipetool') | ||
26 | |||
27 | tinfoil = None | ||
28 | |||
29 | |||
30 | def tinfoil_init(instance): | ||
31 | global tinfoil | ||
32 | tinfoil = instance | ||
33 | |||
34 | |||
35 | |||
36 | class GoRecipeHandler(RecipeHandler): | ||
37 | """Class to handle the go recipe creation""" | ||
38 | |||
39 | @staticmethod | ||
40 | def __ensure_go(): | ||
41 | """Check if the 'go' command is available in the recipes""" | ||
42 | recipe = "go-native" | ||
43 | if not tinfoil.recipes_parsed: | ||
44 | tinfoil.parse_recipes() | ||
45 | try: | ||
46 | rd = tinfoil.parse_recipe(recipe) | ||
47 | except bb.providers.NoProvider: | ||
48 | bb.error( | ||
49 | "Nothing provides '%s' which is required for the build" % (recipe)) | ||
50 | bb.note( | ||
51 | "You will likely need to add a layer that provides '%s'" % (recipe)) | ||
52 | return None | ||
53 | |||
54 | bindir = rd.getVar('STAGING_BINDIR_NATIVE') | ||
55 | gopath = os.path.join(bindir, 'go') | ||
56 | |||
57 | if not os.path.exists(gopath): | ||
58 | tinfoil.build_targets(recipe, 'addto_recipe_sysroot') | ||
59 | |||
60 | if not os.path.exists(gopath): | ||
61 | logger.error( | ||
62 | '%s required to process specified source, but %s did not seem to populate it' % 'go', recipe) | ||
63 | return None | ||
64 | |||
65 | return bindir | ||
66 | |||
67 | def process(self, srctree, classes, lines_before, | ||
68 | lines_after, handled, extravalues): | ||
69 | |||
70 | if 'buildsystem' in handled: | ||
71 | return False | ||
72 | |||
73 | files = RecipeHandler.checkfiles(srctree, ['go.mod']) | ||
74 | if not files: | ||
75 | return False | ||
76 | |||
77 | go_bindir = self.__ensure_go() | ||
78 | if not go_bindir: | ||
79 | sys.exit(14) | ||
80 | |||
81 | handled.append('buildsystem') | ||
82 | classes.append("go-mod") | ||
83 | |||
84 | # Use go-mod-update-modules to set the full SRC_URI and LICENSE | ||
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 | ||
92 | |||
93 | stdout = subprocess.check_output(["go", "mod", "edit", "-json"], cwd=srctree, env=env, text=True) | ||
94 | go_mod = json.loads(stdout) | ||
95 | go_import = re.sub(r'/v([0-9]+)$', '', go_mod['Module']['Path']) | ||
96 | |||
97 | localfilesdir = tempfile.mkdtemp(prefix='recipetool-go-') | ||
98 | extravalues.setdefault('extrafiles', {}) | ||
99 | |||
100 | # Write the stub ${BPN}-licenses.inc and ${BPN}-go-mods.inc files | ||
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 | ||
106 | |||
107 | basename = "{pn}-go-mods.inc" | ||
108 | filename = os.path.join(localfilesdir, basename) | ||
109 | with open(filename, "w") as f: | ||
110 | f.write("# FROM RECIPETOOL\n") | ||
111 | extravalues['extrafiles'][f"../{basename}"] = filename | ||
112 | |||
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) | ||
117 | |||
118 | self.__rewrite_src_uri(lines_before) | ||
119 | |||
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}"') | ||
123 | |||
124 | def __update_lines_before(self, updated, newlines, lines_before): | ||
125 | if updated: | ||
126 | del lines_before[:] | ||
127 | for line in newlines: | ||
128 | # Hack to avoid newlines that edit_metadata inserts | ||
129 | if line.endswith('\n'): | ||
130 | line = line[:-1] | ||
131 | lines_before.append(line) | ||
132 | return updated | ||
133 | |||
134 | def __rewrite_lic_vars(self, lines_before): | ||
135 | def varfunc(varname, origvalue, op, newlines): | ||
136 | import urllib.parse | ||
137 | if varname == 'LIC_FILES_CHKSUM': | ||
138 | new_licenses = [] | ||
139 | licenses = origvalue.split('\\') | ||
140 | for license in licenses: | ||
141 | if not license: | ||
142 | logger.warning("No license file was detected for the main module!") | ||
143 | # the license list of the main recipe must be empty | ||
144 | # this can happen for example in case of CLOSED license | ||
145 | # Fall through to complete recipe generation | ||
146 | continue | ||
147 | license = license.strip() | ||
148 | uri, chksum = license.split(';', 1) | ||
149 | url = urllib.parse.urlparse(uri) | ||
150 | new_uri = os.path.join( | ||
151 | url.scheme + "://", "src", "${GO_IMPORT}", url.netloc + url.path) + ";" + chksum | ||
152 | new_licenses.append(new_uri) | ||
153 | |||
154 | return new_licenses, None, -1, True | ||
155 | return origvalue, None, 0, True | ||
156 | |||
157 | updated, newlines = bb.utils.edit_metadata( | ||
158 | lines_before, ['LIC_FILES_CHKSUM'], varfunc) | ||
159 | return self.__update_lines_before(updated, newlines, lines_before) | ||
160 | |||
161 | def __rewrite_src_uri(self, lines_before): | ||
162 | |||
163 | def varfunc(varname, origvalue, op, newlines): | ||
164 | if varname == 'SRC_URI': | ||
165 | src_uri = ['git://${GO_IMPORT};protocol=https;nobranch=1;destsuffix=${GO_SRCURI_DESTSUFFIX}'] | ||
166 | return src_uri, None, -1, True | ||
167 | return origvalue, None, 0, True | ||
168 | |||
169 | updated, newlines = bb.utils.edit_metadata(lines_before, ['SRC_URI'], varfunc) | ||
170 | return self.__update_lines_before(updated, newlines, lines_before) | ||
171 | |||
172 | |||
173 | def register_recipe_handlers(handlers): | ||
174 | handlers.append((GoRecipeHandler(), 60)) | ||