summaryrefslogtreecommitdiffstats
path: root/scripts/lib/recipetool/create_go.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/lib/recipetool/create_go.py')
-rw-r--r--scripts/lib/recipetool/create_go.py174
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
13from recipetool.create import RecipeHandler, handle_license_vars
14
15import bb.utils
16import json
17import logging
18import os
19import re
20import subprocess
21import sys
22import tempfile
23
24
25logger = logging.getLogger('recipetool')
26
27tinfoil = None
28
29
30def tinfoil_init(instance):
31 global tinfoil
32 tinfoil = instance
33
34
35
36class 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
173def register_recipe_handlers(handlers):
174 handlers.append((GoRecipeHandler(), 60))