| addtask do_update_modules after do_configure |
| do_update_modules[nostamp] = "1" |
| do_update_modules[network] = "1" |
| |
| # This class maintains two files, BPN-go-mods.inc and BPN-licenses.inc. |
| # |
| # -go-mods.inc will append SRC_URI with all of the Go modules that are |
| # dependencies of this recipe. |
| # |
| # -licenses.inc will append LICENSE and LIC_FILES_CHKSUM with the found licenses |
| # in the modules. |
| # |
| # These files are machine-generated and should not be modified. |
| |
| python do_update_modules() { |
| import subprocess, tempfile, json, re, urllib.parse |
| from oe.license import tidy_licenses |
| from oe.license_finder import find_licenses_up |
| |
| def unescape_path(path): |
| """Unescape capital letters using exclamation points.""" |
| return re.sub(r'!([a-z])', lambda m: m.group(1).upper(), path) |
| |
| def fold_uri(uri): |
| """Fold URI for sorting shorter module paths before longer.""" |
| return uri.replace(';', ' ').replace('/', '!') |
| |
| def parse_existing_licenses(): |
| hashes = {} |
| for url in d.getVar("LIC_FILES_CHKSUM").split(): |
| (method, host, path, user, pswd, parm) = bb.fetch.decodeurl(url) |
| if "spdx" in parm and parm["spdx"] != "Unknown": |
| hashes[parm["md5"]] = urllib.parse.unquote_plus(parm["spdx"]) |
| return hashes |
| |
| bpn = d.getVar("BPN") |
| thisdir = d.getVar("THISDIR") |
| s_dir = d.getVar("S") |
| |
| with tempfile.TemporaryDirectory(prefix='go-mod-') as mod_cache_dir: |
| notice = """ |
| # This file has been generated by go-mod-update-modules.bbclass |
| # |
| # Do not modify it by hand, as the contents will be replaced when |
| # running the update-modules task. |
| |
| """ |
| |
| env = dict(os.environ, GOMODCACHE=mod_cache_dir) |
| source = d.expand("${UNPACKDIR}/${GO_SRCURI_DESTSUFFIX}") |
| go_install = d.getVar("GO_INSTALL").split() |
| output = subprocess.check_output(("go", "list", "-json=Dir,Module", "-deps", *go_install), |
| cwd=source, env=env, text=True) |
| |
| # |
| # Licenses |
| # |
| |
| # load hashes from the existing licenses.inc |
| extra_hashes = parse_existing_licenses() |
| |
| # The output of this isn't actually valid JSON, but a series of dicts. |
| # Wrap in [] and join the dicts with , |
| # Very frustrating that the json parser in python can't repeatedly |
| # parse from a stream. |
| pkgs = json.loads('[' + output.replace('}\n{', '},\n{') + ']') |
| |
| # Collect licenses for the dependencies. |
| lic_files = {} |
| for pkg in pkgs: |
| pkg_dir = pkg['Dir'] |
| if not pkg_dir.startswith(mod_cache_dir): |
| continue |
| |
| mod_dir = pkg['Module']['Dir'] |
| path = os.path.relpath(mod_dir, mod_cache_dir) |
| |
| for name, file, md5 in find_licenses_up(pkg_dir, mod_dir, d, first_only=True, extra_hashes=extra_hashes): |
| lic_files[os.path.join(path, file)] = (name, md5) |
| |
| licenses = set() |
| lic_files_chksum = [] |
| for lic_file in lic_files: |
| license_name, license_md5 = lic_files[lic_file] |
| if license_name == "Unknown": |
| bb.warn(f"Unknown license: {lic_file} {license_md5}") |
| |
| licenses.add(lic_files[lic_file][0]) |
| lic_files_chksum.append( |
| f'file://pkg/mod/{lic_file};md5={license_md5};spdx={urllib.parse.quote_plus(license_name)}') |
| |
| licenses_filename = os.path.join(thisdir, f"{bpn}-licenses.inc") |
| with open(licenses_filename, "w") as f: |
| f.write(notice) |
| f.write(f'LICENSE += "& {" & ".join(tidy_licenses(licenses))}"\n\n') |
| f.write('LIC_FILES_CHKSUM += "\\\n') |
| for lic in sorted(lic_files_chksum, key=fold_uri): |
| f.write(' ' + lic + ' \\\n') |
| f.write('"\n') |
| |
| # |
| # Sources |
| # |
| |
| # Collect the module cache files downloaded by the go list command as |
| # the go list command knows best what the go list command needs and it |
| # needs more files in the module cache than the go install command as |
| # it doesn't do the dependency pruning mentioned in the Go module |
| # reference, https://go.dev/ref/mod, for go 1.17 or higher. |
| src_uris = [] |
| downloaddir = os.path.join(mod_cache_dir, 'cache', 'download') |
| for dirpath, _, filenames in os.walk(downloaddir): |
| # We want to process files under @v directories |
| path, base = os.path.split(os.path.relpath(dirpath, downloaddir)) |
| if base != '@v': |
| continue |
| |
| path = unescape_path(path) |
| zipver = None |
| for name in filenames: |
| ver, ext = os.path.splitext(name) |
| if ext == '.zip': |
| chksum = bb.utils.sha256_file(os.path.join(dirpath, name)) |
| src_uris.append(f'gomod://{path};version={ver};sha256sum={chksum}') |
| zipver = ver |
| break |
| for name in filenames: |
| ver, ext = os.path.splitext(name) |
| if ext == '.mod' and ver != zipver: |
| chksum = bb.utils.sha256_file(os.path.join(dirpath, name)) |
| src_uris.append(f'gomod://{path};version={ver};mod=1;sha256sum={chksum}') |
| |
| |
| go_mods_filename = os.path.join(thisdir, f"{bpn}-go-mods.inc") |
| with open(go_mods_filename, "w") as f: |
| f.write(notice) |
| f.write('SRC_URI += "\\\n') |
| for uri in sorted(src_uris, key=fold_uri): |
| f.write(' ' + uri + ' \\\n') |
| f.write('"\n') |
| |
| subprocess.check_output(("go", "clean", "-modcache"), cwd=source, env=env, text=True) |
| } |
| |
| # This doesn't work as we need to wipe the inc files first so we don't try looking for LICENSE files that don't yet exist |
| # RECIPE_UPGRADE_EXTRA_TASKS += "do_update_modules" |