# Recipe creation tool - go support plugin # # The code is based on golang internals. See the afftected # methods for further reference and information. # # Copyright (C) 2023 Weidmueller GmbH & Co KG # Author: Lukas Funke # # SPDX-License-Identifier: GPL-2.0-only # from recipetool.create import RecipeHandler, handle_license_vars import bb.utils import json import logging import os import re import subprocess import sys import tempfile logger = logging.getLogger('recipetool') tinfoil = None def tinfoil_init(instance): global tinfoil tinfoil = instance class GoRecipeHandler(RecipeHandler): """Class to handle the go recipe creation""" @staticmethod def __ensure_go(): """Check if the 'go' command is available in the recipes""" recipe = "go-native" if not tinfoil.recipes_parsed: tinfoil.parse_recipes() try: rd = tinfoil.parse_recipe(recipe) except bb.providers.NoProvider: bb.error( "Nothing provides '%s' which is required for the build" % (recipe)) bb.note( "You will likely need to add a layer that provides '%s'" % (recipe)) return None bindir = rd.getVar('STAGING_BINDIR_NATIVE') gopath = os.path.join(bindir, 'go') if not os.path.exists(gopath): tinfoil.build_targets(recipe, 'addto_recipe_sysroot') if not os.path.exists(gopath): logger.error( '%s required to process specified source, but %s did not seem to populate it' % 'go', recipe) return None return bindir def process(self, srctree, classes, lines_before, lines_after, handled, extravalues): if 'buildsystem' in handled: return False files = RecipeHandler.checkfiles(srctree, ['go.mod']) if not files: return False go_bindir = self.__ensure_go() if not go_bindir: sys.exit(14) handled.append('buildsystem') classes.append("go-mod") # Use go-mod-update-modules to set the full SRC_URI and LICENSE classes.append("go-mod-update-modules") extravalues["run_tasks"] = "update_modules" with tempfile.TemporaryDirectory(prefix="go-mod-") as tmp_mod_dir: env = dict(os.environ) env["PATH"] += f":{go_bindir}" env['GOMODCACHE'] = tmp_mod_dir stdout = subprocess.check_output(["go", "mod", "edit", "-json"], cwd=srctree, env=env, text=True) go_mod = json.loads(stdout) go_import = re.sub(r'/v([0-9]+)$', '', go_mod['Module']['Path']) localfilesdir = tempfile.mkdtemp(prefix='recipetool-go-') extravalues.setdefault('extrafiles', {}) # Write the stub ${BPN}-licenses.inc and ${BPN}-go-mods.inc files basename = "{pn}-licenses.inc" filename = os.path.join(localfilesdir, basename) with open(filename, "w") as f: f.write("# FROM RECIPETOOL\n") extravalues['extrafiles'][f"../{basename}"] = filename basename = "{pn}-go-mods.inc" filename = os.path.join(localfilesdir, basename) with open(filename, "w") as f: f.write("# FROM RECIPETOOL\n") extravalues['extrafiles'][f"../{basename}"] = filename # Do generic license handling d = bb.data.createCopy(tinfoil.config_data) handle_license_vars(srctree, lines_before, handled, extravalues, d) self.__rewrite_lic_vars(lines_before) self.__rewrite_src_uri(lines_before) lines_before.append('require ${BPN}-licenses.inc') lines_before.append('require ${BPN}-go-mods.inc') lines_before.append(f'GO_IMPORT = "{go_import}"') def __update_lines_before(self, updated, newlines, lines_before): if updated: del lines_before[:] for line in newlines: # Hack to avoid newlines that edit_metadata inserts if line.endswith('\n'): line = line[:-1] lines_before.append(line) return updated def __rewrite_lic_vars(self, lines_before): def varfunc(varname, origvalue, op, newlines): import urllib.parse if varname == 'LIC_FILES_CHKSUM': new_licenses = [] licenses = origvalue.split('\\') for license in licenses: if not license: logger.warning("No license file was detected for the main module!") # the license list of the main recipe must be empty # this can happen for example in case of CLOSED license # Fall through to complete recipe generation continue license = license.strip() uri, chksum = license.split(';', 1) url = urllib.parse.urlparse(uri) new_uri = os.path.join( url.scheme + "://", "src", "${GO_IMPORT}", url.netloc + url.path) + ";" + chksum new_licenses.append(new_uri) return new_licenses, None, -1, True return origvalue, None, 0, True updated, newlines = bb.utils.edit_metadata( lines_before, ['LIC_FILES_CHKSUM'], varfunc) return self.__update_lines_before(updated, newlines, lines_before) def __rewrite_src_uri(self, lines_before): def varfunc(varname, origvalue, op, newlines): if varname == 'SRC_URI': src_uri = ['git://${GO_IMPORT};protocol=https;nobranch=1;destsuffix=${GO_SRCURI_DESTSUFFIX}'] return src_uri, None, -1, True return origvalue, None, 0, True updated, newlines = bb.utils.edit_metadata(lines_before, ['SRC_URI'], varfunc) return self.__update_lines_before(updated, newlines, lines_before) def register_recipe_handlers(handlers): handlers.append((GoRecipeHandler(), 60))