diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-04-28 18:49:19 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-04-28 18:49:19 +0000 |
| commit | cd15723c5d62bbfa4c07c36eb7ab7bd962bd4936 (patch) | |
| tree | 0dea471977d0b5c14e1523f2a1a206d9d3fd6137 /classes/go-mod-discovery.bbclass | |
| parent | a66c8df6fdc9025d0cbfae76252e12fc530a4824 (diff) | |
| download | meta-virtualization-cd15723c5d62bbfa4c07c36eb7ab7bd962bd4936.tar.gz | |
oe-go-mod-fetcher: add license scanning for Go module dependencies
Add --scan-licenses to oe-go-mod-fetcher.py which scans Go module zips
for license files and generates go-mod-licenses.inc with LICENSE and
LIC_FILES_CHKSUM entries matching OE-core's go-mod-update-modules format.
License detection uses OE-core's glob patterns and MD5 + crunched MD5
matching against known SPDX licenses. The hash database resolves from:
1. --common-license-dir (explicit path)
2. Auto-detected poky tree common-licenses
3. Bundled scripts/data/license-hashes.csv (offline fallback)
New files:
- scripts/generate-license-hashes.py: regenerate bundled CSV
- scripts/data/license-hashes.csv: pre-computed hash DB (704 entries)
bbclass changes:
- go-mod-discovery: pass --scan-licenses during do_generate_modules
- GO_MOD_DISCOVERY_SKIP_LICENSES variable to bypass scanning
- do_update_license_hashes task to refresh bundled CSV
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'classes/go-mod-discovery.bbclass')
| -rw-r--r-- | classes/go-mod-discovery.bbclass | 88 |
1 files changed, 84 insertions, 4 deletions
diff --git a/classes/go-mod-discovery.bbclass b/classes/go-mod-discovery.bbclass index 39cc38da..9609ee35 100644 --- a/classes/go-mod-discovery.bbclass +++ b/classes/go-mod-discovery.bbclass | |||
| @@ -32,6 +32,10 @@ | |||
| 32 | # bitbake <recipe> -c clean_discovery | 32 | # bitbake <recipe> -c clean_discovery |
| 33 | # Remove the persistent discovery cache | 33 | # Remove the persistent discovery cache |
| 34 | # | 34 | # |
| 35 | # bitbake <recipe> -c update_license_hashes | ||
| 36 | # Regenerate the bundled license-hashes.csv from COMMON_LICENSE_DIR | ||
| 37 | # Run after OE-core upgrades to keep standalone CSV current | ||
| 38 | # | ||
| 35 | # CONFIGURATION: | 39 | # CONFIGURATION: |
| 36 | # | 40 | # |
| 37 | # Required (must be set by recipe): | 41 | # Required (must be set by recipe): |
| @@ -74,6 +78,9 @@ | |||
| 74 | # GO_MOD_DISCOVERY_RECIPEDIR - Output directory for generated .inc files | 78 | # GO_MOD_DISCOVERY_RECIPEDIR - Output directory for generated .inc files |
| 75 | # Default: "${FILE_DIRNAME}" (recipe's directory) | 79 | # Default: "${FILE_DIRNAME}" (recipe's directory) |
| 76 | # | 80 | # |
| 81 | # GO_MOD_DISCOVERY_SKIP_LICENSES - Set to "1" to skip license scanning | ||
| 82 | # Default: "" (scan enabled) | ||
| 83 | # | ||
| 77 | # WORKFLOW EXAMPLES: | 84 | # WORKFLOW EXAMPLES: |
| 78 | # | 85 | # |
| 79 | # Full automatic (one command does everything): | 86 | # Full automatic (one command does everything): |
| @@ -122,6 +129,11 @@ GO_MOD_DISCOVERY_RECIPEDIR ?= "${FILE_DIRNAME}" | |||
| 122 | # Usage: GO_MOD_DISCOVERY_SKIP_VERIFY = "1" in local.conf or recipe | 129 | # Usage: GO_MOD_DISCOVERY_SKIP_VERIFY = "1" in local.conf or recipe |
| 123 | GO_MOD_DISCOVERY_SKIP_VERIFY ?= "" | 130 | GO_MOD_DISCOVERY_SKIP_VERIFY ?= "" |
| 124 | 131 | ||
| 132 | # Skip license scanning during generation | ||
| 133 | # Set to "1" to bypass license scanning (default: scan licenses) | ||
| 134 | # Usage: GO_MOD_DISCOVERY_SKIP_LICENSES = "1" in local.conf or recipe | ||
| 135 | GO_MOD_DISCOVERY_SKIP_LICENSES ?= "" | ||
| 136 | |||
| 125 | # Modules to exclude from git:// generation (space-separated prefixes) | 137 | # Modules to exclude from git:// generation (space-separated prefixes) |
| 126 | # Excluded modules must be fetched via gomod:// in the recipe's SRC_URI | 138 | # Excluded modules must be fetched via gomod:// in the recipe's SRC_URI |
| 127 | # Usage: GO_MOD_VCS_EXCLUDE = "github.com/vtolstov/go-ioctl" | 139 | # Usage: GO_MOD_VCS_EXCLUDE = "github.com/vtolstov/go-ioctl" |
| @@ -406,13 +418,19 @@ Or run 'bitbake ${PN} -c show_upgrade_commands' to see manual options." | |||
| 406 | EXCLUDE_FLAGS="${EXCLUDE_FLAGS} --exclude-module ${mod}" | 418 | EXCLUDE_FLAGS="${EXCLUDE_FLAGS} --exclude-module ${mod}" |
| 407 | done | 419 | done |
| 408 | 420 | ||
| 421 | LICENSE_FLAGS="" | ||
| 422 | if [ "${GO_MOD_DISCOVERY_SKIP_LICENSES}" != "1" ]; then | ||
| 423 | LICENSE_FLAGS="--scan-licenses --common-license-dir ${COMMON_LICENSE_DIR} --discovery-cache ${GO_MOD_DISCOVERY_DIR}/cache" | ||
| 424 | fi | ||
| 425 | |||
| 409 | python3 "${FETCHER_SCRIPT}" \ | 426 | python3 "${FETCHER_SCRIPT}" \ |
| 410 | --discovered-modules "${GO_MOD_DISCOVERY_MODULES_JSON}" \ | 427 | --discovered-modules "${GO_MOD_DISCOVERY_MODULES_JSON}" \ |
| 411 | --git-repo "${GO_MOD_DISCOVERY_GIT_REPO}" \ | 428 | --git-repo "${GO_MOD_DISCOVERY_GIT_REPO}" \ |
| 412 | --git-ref "${GO_MOD_DISCOVERY_GIT_REF}" \ | 429 | --git-ref "${GO_MOD_DISCOVERY_GIT_REF}" \ |
| 413 | --recipedir "${GO_MOD_DISCOVERY_RECIPEDIR}" \ | 430 | --recipedir "${GO_MOD_DISCOVERY_RECIPEDIR}" \ |
| 414 | ${SKIP_VERIFY_FLAG} \ | 431 | ${SKIP_VERIFY_FLAG} \ |
| 415 | ${EXCLUDE_FLAGS} | 432 | ${EXCLUDE_FLAGS} \ |
| 433 | ${LICENSE_FLAGS} | ||
| 416 | 434 | ||
| 417 | if [ $? -eq 0 ]; then | 435 | if [ $? -eq 0 ]; then |
| 418 | echo "" | 436 | echo "" |
| @@ -422,6 +440,7 @@ Or run 'bitbake ${PN} -c show_upgrade_commands' to see manual options." | |||
| 422 | echo "Files generated in: ${GO_MOD_DISCOVERY_RECIPEDIR}" | 440 | echo "Files generated in: ${GO_MOD_DISCOVERY_RECIPEDIR}" |
| 423 | echo " - go-mod-git.inc" | 441 | echo " - go-mod-git.inc" |
| 424 | echo " - go-mod-cache.inc" | 442 | echo " - go-mod-cache.inc" |
| 443 | echo " - go-mod-licenses.inc (if license scanning enabled)" | ||
| 425 | echo "" | 444 | echo "" |
| 426 | echo "You can now build the recipe:" | 445 | echo "You can now build the recipe:" |
| 427 | echo " bitbake ${PN}" | 446 | echo " bitbake ${PN}" |
| @@ -434,7 +453,7 @@ Or run 'bitbake ${PN} -c show_upgrade_commands' to see manual options." | |||
| 434 | addtask generate_modules | 453 | addtask generate_modules |
| 435 | do_generate_modules[nostamp] = "1" | 454 | do_generate_modules[nostamp] = "1" |
| 436 | do_generate_modules[vardeps] += "GO_MOD_DISCOVERY_MODULES_JSON GO_MOD_DISCOVERY_GIT_REPO \ | 455 | do_generate_modules[vardeps] += "GO_MOD_DISCOVERY_MODULES_JSON GO_MOD_DISCOVERY_GIT_REPO \ |
| 437 | GO_MOD_DISCOVERY_GIT_REF GO_MOD_DISCOVERY_RECIPEDIR" | 456 | GO_MOD_DISCOVERY_GIT_REF GO_MOD_DISCOVERY_RECIPEDIR GO_MOD_DISCOVERY_SKIP_LICENSES" |
| 438 | do_generate_modules[postfuncs] = "do_show_hybrid_recommendation" | 457 | do_generate_modules[postfuncs] = "do_show_hybrid_recommendation" |
| 439 | 458 | ||
| 440 | # Show hybrid conversion recommendations after VCS generation | 459 | # Show hybrid conversion recommendations after VCS generation |
| @@ -627,12 +646,22 @@ python do_show_upgrade_commands() { | |||
| 627 | bb.plain(f" --git-ref {git_ref} \\") | 646 | bb.plain(f" --git-ref {git_ref} \\") |
| 628 | bb.plain(f" --recipedir {recipedir}") | 647 | bb.plain(f" --recipedir {recipedir}") |
| 629 | bb.plain("") | 648 | bb.plain("") |
| 649 | bb.plain("Add --scan-licenses to also generate go-mod-licenses.inc:") | ||
| 650 | bb.plain("") | ||
| 651 | bb.plain(f" {fetcher_script} \\") | ||
| 652 | bb.plain(f" --discovered-modules {modules_json} \\") | ||
| 653 | bb.plain(f" --git-repo {git_repo} \\") | ||
| 654 | bb.plain(f" --git-ref {git_ref} \\") | ||
| 655 | bb.plain(f" --recipedir {recipedir} \\") | ||
| 656 | bb.plain(f" --scan-licenses --discovery-cache {discovery_dir}/cache") | ||
| 657 | bb.plain("") | ||
| 630 | bb.plain("") | 658 | bb.plain("") |
| 631 | bb.plain("Generated files:") | 659 | bb.plain("Generated files:") |
| 632 | bb.plain("-" * 70) | 660 | bb.plain("-" * 70) |
| 633 | bb.plain("") | 661 | bb.plain("") |
| 634 | bb.plain(" go-mod-git.inc - SRC_URI entries for fetching module git repos") | 662 | bb.plain(" go-mod-git.inc - SRC_URI entries for fetching module git repos") |
| 635 | bb.plain(" go-mod-cache.inc - Module path mappings for cache creation") | 663 | bb.plain(" go-mod-cache.inc - Module path mappings for cache creation") |
| 664 | bb.plain(" go-mod-licenses.inc - LICENSE and LIC_FILES_CHKSUM for dependencies") | ||
| 636 | bb.plain("") | 665 | bb.plain("") |
| 637 | bb.plain("=" * 70) | 666 | bb.plain("=" * 70) |
| 638 | bb.plain("") | 667 | bb.plain("") |
| @@ -642,3 +671,54 @@ addtask show_upgrade_commands | |||
| 642 | do_show_upgrade_commands[nostamp] = "1" | 671 | do_show_upgrade_commands[nostamp] = "1" |
| 643 | do_show_upgrade_commands[vardeps] += "GO_MOD_DISCOVERY_GIT_REPO GO_MOD_DISCOVERY_GIT_REF \ | 672 | do_show_upgrade_commands[vardeps] += "GO_MOD_DISCOVERY_GIT_REPO GO_MOD_DISCOVERY_GIT_REF \ |
| 644 | GO_MOD_DISCOVERY_RECIPEDIR GO_MOD_DISCOVERY_DIR GO_MOD_DISCOVERY_MODULES_JSON" | 673 | GO_MOD_DISCOVERY_RECIPEDIR GO_MOD_DISCOVERY_DIR GO_MOD_DISCOVERY_MODULES_JSON" |
| 674 | |||
| 675 | # ============================================================================= | ||
| 676 | # TASK: do_update_license_hashes - Regenerate bundled license hash CSV | ||
| 677 | # ============================================================================= | ||
| 678 | # Regenerates scripts/data/license-hashes.csv from ${COMMON_LICENSE_DIR}. | ||
| 679 | # Run after OE-core upgrades to keep the bundled CSV current for standalone users. | ||
| 680 | # | ||
| 681 | # Usage: bitbake <any-go-recipe> -c update_license_hashes | ||
| 682 | # | ||
| 683 | python do_update_license_hashes() { | ||
| 684 | import subprocess | ||
| 685 | from pathlib import Path | ||
| 686 | |||
| 687 | common_license_dir = d.getVar('COMMON_LICENSE_DIR') | ||
| 688 | if not common_license_dir or not os.path.isdir(common_license_dir): | ||
| 689 | bb.fatal(f"COMMON_LICENSE_DIR not found: {common_license_dir}") | ||
| 690 | |||
| 691 | # Find the generator script and output path | ||
| 692 | layerdir = None | ||
| 693 | for layer in d.getVar('BBLAYERS').split(): | ||
| 694 | candidate = os.path.join(layer, 'scripts', 'generate-license-hashes.py') | ||
| 695 | if os.path.exists(candidate): | ||
| 696 | layerdir = layer | ||
| 697 | break | ||
| 698 | |||
| 699 | if not layerdir: | ||
| 700 | bb.fatal("Could not find generate-license-hashes.py in any layer") | ||
| 701 | |||
| 702 | script = os.path.join(layerdir, 'scripts', 'generate-license-hashes.py') | ||
| 703 | output = os.path.join(layerdir, 'scripts', 'data', 'license-hashes.csv') | ||
| 704 | |||
| 705 | bb.plain(f"Regenerating {output} from {common_license_dir}") | ||
| 706 | |||
| 707 | result = subprocess.run( | ||
| 708 | ['python3', script, common_license_dir], | ||
| 709 | capture_output=True, text=True, timeout=60 | ||
| 710 | ) | ||
| 711 | |||
| 712 | if result.returncode != 0: | ||
| 713 | bb.fatal(f"generate-license-hashes.py failed: {result.stderr}") | ||
| 714 | |||
| 715 | os.makedirs(os.path.dirname(output), exist_ok=True) | ||
| 716 | with open(output, 'w') as f: | ||
| 717 | f.write(result.stdout) | ||
| 718 | |||
| 719 | lines = [l for l in result.stdout.splitlines() if l and not l.startswith('#')] | ||
| 720 | bb.plain(f"Generated {len(lines)} license hash entries in {output}") | ||
| 721 | } | ||
| 722 | |||
| 723 | addtask update_license_hashes | ||
| 724 | do_update_license_hashes[nostamp] = "1" | ||
