summaryrefslogtreecommitdiffstats
path: root/scripts/oe-go-mod-autogen.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/oe-go-mod-autogen.py')
-rwxr-xr-xscripts/oe-go-mod-autogen.py141
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
27from collections import OrderedDict 27from collections import OrderedDict
28import subprocess 28import subprocess
29import textwrap 29import textwrap
30import 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
32ERROR_OUT_ON_FETCH_AND_CHECKOUT_FAILURE = True 33ERROR_OUT_ON_FETCH_AND_CHECKOUT_FAILURE = False
33 34
34logger = logging.getLogger('oe-go-mod-autogen') 35logger = logging.getLogger('oe-go-mod-autogen')
35loggerhandler = logging.StreamHandler() 36loggerhandler = 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"
504SRCREV_%s="%s" 572 template = """# [%s %s] git ls-remote %s %s
505SRC_URI += "git://%s;name=%s;protocol=https;nobranch=1;destsuffix=${WORKDIR}/${BP}/src/import/vendor.fetch/%s" 573SRCREV_%s = "%s"
574SRC_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