diff options
Diffstat (limited to 'scripts/oe-go-mod-autogen.py')
-rwxr-xr-x | scripts/oe-go-mod-autogen.py | 141 |
1 files changed, 123 insertions, 18 deletions
diff --git a/scripts/oe-go-mod-autogen.py b/scripts/oe-go-mod-autogen.py index c44c096a..7cb101d0 100755 --- a/scripts/oe-go-mod-autogen.py +++ b/scripts/oe-go-mod-autogen.py | |||
@@ -27,9 +27,10 @@ import argparse | |||
27 | from collections import OrderedDict | 27 | from collections import OrderedDict |
28 | import subprocess | 28 | import subprocess |
29 | import textwrap | 29 | import textwrap |
30 | import re | ||
30 | 31 | ||
31 | # This switch is used to make this script error out ASAP, mainly for debugging purpose | 32 | # This switch is used to make this script error out ASAP, mainly for debugging purpose |
32 | ERROR_OUT_ON_FETCH_AND_CHECKOUT_FAILURE = True | 33 | ERROR_OUT_ON_FETCH_AND_CHECKOUT_FAILURE = False |
33 | 34 | ||
34 | logger = logging.getLogger('oe-go-mod-autogen') | 35 | logger = logging.getLogger('oe-go-mod-autogen') |
35 | loggerhandler = logging.StreamHandler() | 36 | loggerhandler = logging.StreamHandler() |
@@ -104,11 +105,11 @@ class GoModTool(object): | |||
104 | # check if this repo needs autogen | 105 | # check if this repo needs autogen |
105 | repo_url, repo_dest_dir, repo_fullrev = self.modules_repoinfo[self.repo.split('://')[1]] | 106 | repo_url, repo_dest_dir, repo_fullrev = self.modules_repoinfo[self.repo.split('://')[1]] |
106 | if os.path.isdir(os.path.join(repo_dest_dir, 'vendor')): | 107 | if os.path.isdir(os.path.join(repo_dest_dir, 'vendor')): |
107 | logger.info("vendor direcotry has already existed for %s, no need to add other repos" % self.repo) | 108 | logger.info("vendor directory already exists for %s, no need to add other repos" % self.repo) |
108 | return | 109 | return |
109 | go_mod_file = os.path.join(repo_dest_dir, 'go.mod') | 110 | go_mod_file = os.path.join(repo_dest_dir, 'go.mod') |
110 | if not os.path.exists(go_mod_file): | 111 | if not os.path.exists(go_mod_file): |
111 | logger.info("go.mod file does not exist for %s, no need to add otehr repos" % self.repo) | 112 | logger.info("go.mod file does not exist for %s, no need to add other repos" % self.repo) |
112 | return | 113 | return |
113 | self.parse_go_mod(go_mod_file) | 114 | self.parse_go_mod(go_mod_file) |
114 | self.show_go_mod_info() | 115 | self.show_go_mod_info() |
@@ -256,6 +257,7 @@ class GoModTool(object): | |||
256 | self.modules_repoinfo[module_name] = (repo_url, repo_dest_dir, requiredrev) | 257 | self.modules_repoinfo[module_name] = (repo_url, repo_dest_dir, requiredrev) |
257 | else: | 258 | else: |
258 | logger.warning("Failed to get requiredrev, repo_url = %s, rev = %s, module_name = %s" % (repo_url, rev, module_name)) | 259 | logger.warning("Failed to get requiredrev, repo_url = %s, rev = %s, module_name = %s" % (repo_url, rev, module_name)) |
260 | return None | ||
259 | 261 | ||
260 | def parse_go_mod(self, go_mod_path): | 262 | def parse_go_mod(self, go_mod_path): |
261 | """ | 263 | """ |
@@ -302,12 +304,19 @@ class GoModTool(object): | |||
302 | # with the version 'v0.5.5-0.20211029085301-ec551be6f75c'. | 304 | # with the version 'v0.5.5-0.20211029085301-ec551be6f75c'. |
303 | # So the destdir is vendor/github.com/hashicorp/golang-lru while the contents are from github.com/ktock/golang-lru | 305 | # So the destdir is vendor/github.com/hashicorp/golang-lru while the contents are from github.com/ktock/golang-lru |
304 | for line in self.replace_lines: | 306 | for line in self.replace_lines: |
305 | orig_module, actual = line.split('=>') | 307 | try: |
306 | actual_module, actual_version = actual.split() | 308 | orig_module, actual = line.split('=>') |
307 | orig_module = orig_module.strip() | 309 | print( f"replace line: orig: {orig_module} actual_version: {actual}") |
308 | actual_module = actual_module.strip() | 310 | actual_module, actual_version = actual.split() |
309 | actual_version = actual_version.strip() | 311 | print( f"replace line: actual: {actual_module} actual_version: {actual_version}") |
310 | self.modules_replace[orig_module] = (actual_module, actual_version) | 312 | orig_module = orig_module.strip() |
313 | actual_module = actual_module.strip() | ||
314 | actual_version = actual_version.strip() | ||
315 | self.modules_replace[orig_module] = (actual_module, actual_version) | ||
316 | except Exception as e: | ||
317 | print( f"exception {e} caught while parsing, ignoring line: {line}") | ||
318 | # sys.exit(1) | ||
319 | continue | ||
311 | # | 320 | # |
312 | # Typical require lines are as below: | 321 | # Typical require lines are as below: |
313 | # github.com/Masterminds/semver/v3 v3.1.1 | 322 | # github.com/Masterminds/semver/v3 v3.1.1 |
@@ -336,10 +345,14 @@ class GoModTool(object): | |||
336 | # destdir: ${WORKDIR}/${BP}/src/import/vendor.fetch/github.com/Masterminds/semver/v3 | 345 | # destdir: ${WORKDIR}/${BP}/src/import/vendor.fetch/github.com/Masterminds/semver/v3 |
337 | # fullsrcrev: 7bb0c843b53d6ad21a3f619cb22c4b442bb3ef3e (git rev-list -1 v3.1.1) | 346 | # fullsrcrev: 7bb0c843b53d6ad21a3f619cb22c4b442bb3ef3e (git rev-list -1 v3.1.1) |
338 | # | 347 | # |
339 | # As a last resort, if the last component of <module_name> matches 'v[0-9]+', | 348 | # Next, if the last component of <module_name> matches 'v[0-9]+', |
340 | # remove the last component and try wget https://<module_name_with_last_component_removed>?go-get=1, | 349 | # remove the last component and try wget https://<module_name_with_last_component_removed>?go-get=1, |
341 | # then try using the above matching method. | 350 | # then try using the above matching method. |
342 | # | 351 | # |
352 | # Finally, we have a mapping of known modules to source trees that can | ||
353 | # be used to translate the go.mod entry to a repository. Currently this is | ||
354 | # part of the script, but could be read from .map files in the future. | ||
355 | # | ||
343 | for line in self.require_lines: | 356 | for line in self.require_lines: |
344 | module_name, version = line.strip().split() | 357 | module_name, version = line.strip().split() |
345 | logger.debug("require line: %s" % line) | 358 | logger.debug("require line: %s" % line) |
@@ -410,6 +423,10 @@ class GoModTool(object): | |||
410 | if newline != '' and not newline.startswith('<'): | 423 | if newline != '' and not newline.startswith('<'): |
411 | repo_url = newline | 424 | repo_url = newline |
412 | repo_url_found = True | 425 | repo_url_found = True |
426 | if "Repository URL not available" in repo_url: | ||
427 | repo_url_found = False | ||
428 | repo_url = "" | ||
429 | |||
413 | break | 430 | break |
414 | if repo_url_found: | 431 | if repo_url_found: |
415 | logger.info("repo url for %s: %s" % (module_name, repo_url)) | 432 | logger.info("repo url for %s: %s" % (module_name, repo_url)) |
@@ -419,11 +436,62 @@ class GoModTool(object): | |||
419 | else: | 436 | else: |
420 | unhandled_reason = 'cannot determine repo_url for %s' % module_name | 437 | unhandled_reason = 'cannot determine repo_url for %s' % module_name |
421 | self.modules_unhandled[module_name] = unhandled_reason | 438 | self.modules_unhandled[module_name] = unhandled_reason |
422 | return None | 439 | # This used to return, but we have the mapping step below to try |
440 | # as a final resort, leaving this here in case compatiblity issues | ||
441 | # arrive later due to the continued processing. | ||
442 | # return None | ||
423 | except: | 443 | except: |
424 | logger.info("wget -O %s https://pkg.go.dev/%s failed" % (wget_content_file, module_name)) | 444 | logger.info("wget -O %s https://pkg.go.dev/%s failed" % (wget_content_file, module_name)) |
445 | |||
446 | # Do we recognize this twice failed lookup ? | ||
447 | site_mapper = { "inet.af" : { "match" : re.compile(""), | ||
448 | "replace" : "" | ||
449 | } | ||
450 | } | ||
451 | |||
452 | # module name: inet.af/tcpproxy | ||
453 | # replacement: https://github.com/inetaf/tcpproxy | ||
454 | site_mapper["inet.af"]["match"] = re.compile(r"(inet\.af)/(.*)") | ||
455 | site_mapper["inet.af"]["replace"] = "https://github.com/inetaf/\\g<2>" | ||
456 | |||
457 | host, _, _ = module_name.partition('/') | ||
458 | |||
459 | ## on failure, we could consider instructing the user to write their | ||
460 | ## own url into the repo_url_cache file | ||
461 | ## | ||
462 | ## or we could look for a .repo_mapping file, and read/use it to do | ||
463 | ## the mapping and carry that around per-project. | ||
464 | logger.info( "trying mapper lookup for %s (host: %s)" % (module_name,host)) | ||
465 | |||
466 | try: | ||
467 | mapper = site_mapper[host] | ||
468 | m = mapper["match"].match(module_name) | ||
469 | repo_url = m.expand( mapper["replace"] ) | ||
470 | |||
471 | logger.info( "mapper match for %s, returning %s" % (module_name,repo_url) ) | ||
472 | #print( "new site: %s" % repo_url ) | ||
473 | |||
474 | # clear any potentially staged reasons for failures above | ||
475 | self.modules_unhandled[module_name] = "" | ||
476 | |||
477 | with open(url_cache_file, 'w') as f: | ||
478 | f.write(repo_url) | ||
479 | return repo_url | ||
480 | except Exception as e: | ||
481 | unhandled_reason = 'cannot determine mapped repo_url for %s' % module_name | ||
482 | ### XXXX: TODO. if there are more parts to be popped, we shouldn't give up | ||
483 | ### on th emodule | ||
484 | #### | ||
485 | #### and/ or check if there was already an entry from above, since that means | ||
486 | #### there was a more critcal error during the check and we should just | ||
487 | #### propagate the unhandled to the caller | ||
488 | #### | ||
489 | self.modules_unhandled[module_name] = unhandled_reason | ||
490 | del self.modules_unhandled[module_name] | ||
491 | logger.info( "no mapper match, returning none: %s" % e ) | ||
425 | return None | 492 | return None |
426 | 493 | ||
494 | return None | ||
427 | 495 | ||
428 | def get_repo_url_rev(self, module_name, version): | 496 | def get_repo_url_rev(self, module_name, version): |
429 | """ | 497 | """ |
@@ -499,10 +567,11 @@ class GoModTool(object): | |||
499 | src_uri_inc_file = os.path.join(self.workdir, 'src_uri.inc') | 567 | src_uri_inc_file = os.path.join(self.workdir, 'src_uri.inc') |
500 | # record the <name> after writting SRCREV_<name>, this is to avoid modules having the same basename resulting in same SRCREV_xxx | 568 | # record the <name> after writting SRCREV_<name>, this is to avoid modules having the same basename resulting in same SRCREV_xxx |
501 | srcrev_name_recorded = [] | 569 | srcrev_name_recorded = [] |
502 | template = """# %s %s | 570 | # pre styhead releases |
503 | # [1] git ls-remote %s %s | 571 | # SRC_URI += "git://%s;name=%s;protocol=https;nobranch=1;destsuffix=${WORKDIR}/${BP}/src/import/vendor.fetch/%s" |
504 | SRCREV_%s="%s" | 572 | template = """# [%s %s] git ls-remote %s %s |
505 | SRC_URI += "git://%s;name=%s;protocol=https;nobranch=1;destsuffix=${WORKDIR}/${BP}/src/import/vendor.fetch/%s" | 573 | SRCREV_%s = "%s" |
574 | SRC_URI += "git://%s;name=%s;protocol=https;nobranch=1;destsuffix=${GO_SRCURI_DESTSUFFIX}/vendor.fetch/%s" | ||
506 | 575 | ||
507 | """ | 576 | """ |
508 | # We can't simply write SRC_URIs one by one in the order that go.mod specify them. | 577 | # We can't simply write SRC_URIs one by one in the order that go.mod specify them. |
@@ -533,7 +602,10 @@ SRC_URI += "git://%s;name=%s;protocol=https;nobranch=1;destsuffix=${WORKDIR}/${B | |||
533 | # sort the src_uri_contents and then write it | 602 | # sort the src_uri_contents and then write it |
534 | src_uri_contents.sort(key=take_first_len) | 603 | src_uri_contents.sort(key=take_first_len) |
535 | for content in src_uri_contents: | 604 | for content in src_uri_contents: |
536 | f.write(template % content) | 605 | try: |
606 | f.write(template % content) | ||
607 | except Exception as e: | ||
608 | logger.warning( "exception while writing src_uri.inc: %s" % e ) | ||
537 | logger.info("%s generated" % src_uri_inc_file) | 609 | logger.info("%s generated" % src_uri_inc_file) |
538 | 610 | ||
539 | def gen_relocation_inc(self): | 611 | def gen_relocation_inc(self): |
@@ -549,14 +621,40 @@ do_compile:prepend() { | |||
549 | site_dest=$(echo $s | cut -d: -f1) | 621 | site_dest=$(echo $s | cut -d: -f1) |
550 | site_source=$(echo $s | cut -d: -f2) | 622 | site_source=$(echo $s | cut -d: -f2) |
551 | force_flag=$(echo $s | cut -d: -f3) | 623 | force_flag=$(echo $s | cut -d: -f3) |
624 | |||
552 | mkdir -p vendor.copy/$site_dest | 625 | mkdir -p vendor.copy/$site_dest |
626 | |||
627 | # create a temporary exclude file | ||
628 | exclude_file=$(mktemp) | ||
629 | |||
630 | find vendor.fetch/$site_source -type d -print0 | \ | ||
631 | xargs -0 du -sBM 2>/dev/null | \ | ||
632 | awk '{if ($1+0 > 500) print substr($0, index($0,$2))}' | \ | ||
633 | sed 's|^vendor.fetch/||' > "$exclude_file" | ||
634 | |||
553 | if [ -n "$force_flag" ]; then | 635 | if [ -n "$force_flag" ]; then |
554 | echo "[INFO] $site_dest: force copying .go files" | 636 | echo "[INFO] $site_dest: force copying .go files" |
555 | rm -rf vendor.copy/$site_dest | 637 | rm -rf vendor.copy/$site_dest |
556 | rsync -a --exclude='vendor/' --exclude='.git/' vendor.fetch/$site_source/ vendor.copy/$site_dest | 638 | rsync -a \ |
639 | --exclude='vendor/' \ | ||
640 | --exclude='.git/' \ | ||
641 | --exclude-from="$exclude_file" \ | ||
642 | vendor.fetch/$site_source/ vendor.copy/$site_dest | ||
557 | else | 643 | else |
558 | [ -n "$(ls -A vendor.copy/$site_dest/*.go 2> /dev/null)" ] && { echo "[INFO] vendor.fetch/$site_source -> $site_dest: go copy skipped (files present)" ; true ; } || { echo "[INFO] $site_dest: copying .go files" ; rsync -a --exclude='vendor/' --exclude='.git/' vendor.fetch/$site_source/ vendor.copy/$site_dest ; } | 644 | if [ -n "$(ls -A vendor.copy/$site_dest/*.go 2> /dev/null)" ]; then |
645 | echo "[INFO] vendor.fetch/$site_source -> $site_dest: go copy skipped (files present)" | ||
646 | true | ||
647 | else | ||
648 | echo "[INFO] $site_dest: copying .go files" | ||
649 | rsync -a \ | ||
650 | --exclude='vendor/' \ | ||
651 | --exclude='.git/' \ | ||
652 | --exclude-from="$exclude_file" \ | ||
653 | vendor.fetch/$site_source/ vendor.copy/$site_dest | ||
654 | fi | ||
559 | fi | 655 | fi |
656 | |||
657 | rm -f "$exclude_file" | ||
560 | done | 658 | done |
561 | } | 659 | } |
562 | """ | 660 | """ |
@@ -672,6 +770,13 @@ def main(): | |||
672 | directory). If go.mod is edited, modules.txt also has to be | 770 | directory). If go.mod is edited, modules.txt also has to be |
673 | updated to match the revision information. | 771 | updated to match the revision information. |
674 | 772 | ||
773 | Note 4: if an entry in go.mod is resolving to a destination that doesn't | ||
774 | have a SRCREV (i.e. golang.org vs github), the destination can | ||
775 | be temporarily overriden by editing: wget-contents/<repo>.repo_url.cache | ||
776 | The next run will use the cached value versus looking it up. | ||
777 | |||
778 | % vi wget-contents/golang.org_x_sys.repo_url.cache | ||
779 | |||
675 | How to use in a recipe: | 780 | How to use in a recipe: |
676 | ======================= | 781 | ======================= |
677 | 782 | ||