mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 12:59:02 +02:00

Create missing directories required to create the symlink. Use relative symlinks to stay in the build directory scope (to avoid sstate issue with absolute paths). (From OE-Core rev: 6c4e3206b6537f714416688cf8c921dd21cdfc20) Signed-off-by: Alexander Yurkov <alexander.v.yurkov@gmail.com> Signed-off-by: Vyacheslav Yurkov <uvv.mail@gmail.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
216 lines
7.8 KiB
Plaintext
216 lines
7.8 KiB
Plaintext
#
|
|
# Copyright 2023 (C) Weidmueller GmbH & Co KG
|
|
# Author: Lukas Funke <lukas.funke@weidmueller.com>
|
|
#
|
|
# Handle Go vendor support for offline builds
|
|
#
|
|
# When importing Go modules, Go downloads the imported modules using
|
|
# a network (proxy) connection ahead of the compile stage. This contradicts
|
|
# the yocto build concept of fetching every source ahead of build-time
|
|
# and supporting offline builds.
|
|
#
|
|
# To support offline builds, we use Go 'vendoring': module dependencies are
|
|
# downloaded during the fetch-phase and unpacked into the modules 'vendor'
|
|
# folder. Additionally a manifest file is generated for the 'vendor' folder
|
|
#
|
|
|
|
inherit go-mod
|
|
|
|
def go_src_uri(repo, version, path=None, subdir=None, \
|
|
vcs='git', replaces=None, pathmajor=None):
|
|
|
|
destsuffix = "git/src/import/vendor.fetch"
|
|
module_path = repo if not path else path
|
|
|
|
src_uri = "{}://{};name={}".format(vcs, repo, module_path.replace('/', '.'))
|
|
src_uri += ";destsuffix={}/{}@{}".format(destsuffix, repo, version)
|
|
|
|
if vcs == "git":
|
|
src_uri += ";nobranch=1;protocol=https"
|
|
|
|
src_uri += ";go_module_path={}".format(module_path)
|
|
|
|
if replaces:
|
|
src_uri += ";go_module_replacement={}".format(replaces)
|
|
if subdir:
|
|
src_uri += ";go_subdir={}".format(subdir)
|
|
if pathmajor:
|
|
src_uri += ";go_pathmajor={}".format(pathmajor)
|
|
src_uri += ";is_go_dependency=1"
|
|
|
|
return src_uri
|
|
|
|
python do_vendor_unlink() {
|
|
go_import = d.getVar('GO_IMPORT')
|
|
source_dir = d.getVar('S')
|
|
linkname = os.path.join(source_dir, *['src', go_import, 'vendor'])
|
|
|
|
os.unlink(linkname)
|
|
}
|
|
|
|
addtask vendor_unlink before do_package after do_install
|
|
|
|
python do_go_vendor() {
|
|
import shutil
|
|
|
|
src_uri = (d.getVar('SRC_URI') or "").split()
|
|
|
|
if not src_uri:
|
|
bb.fatal("SRC_URI is empty")
|
|
|
|
default_destsuffix = "git/src/import/vendor.fetch"
|
|
fetcher = bb.fetch2.Fetch(src_uri, d)
|
|
go_import = d.getVar('GO_IMPORT')
|
|
source_dir = d.getVar('S')
|
|
|
|
linkname = os.path.join(source_dir, *['src', go_import, 'vendor'])
|
|
vendor_dir = os.path.join(source_dir, *['src', 'import', 'vendor'])
|
|
import_dir = os.path.join(source_dir, *['src', 'import', 'vendor.fetch'])
|
|
|
|
if os.path.exists(vendor_dir):
|
|
# Nothing to do except re-establish link to actual vendor folder
|
|
if not os.path.exists(linkname):
|
|
oe.path.relsymlink(vendor_dir, linkname)
|
|
return
|
|
|
|
bb.utils.mkdirhier(vendor_dir)
|
|
|
|
modules = {}
|
|
|
|
for url in fetcher.urls:
|
|
srcuri = fetcher.ud[url].host + fetcher.ud[url].path
|
|
|
|
# Skip non Go module src uris
|
|
if not fetcher.ud[url].parm.get('is_go_dependency'):
|
|
continue
|
|
|
|
destsuffix = fetcher.ud[url].parm.get('destsuffix')
|
|
# We derive the module repo / version in the following manner (exmaple):
|
|
#
|
|
# destsuffix = git/src/import/vendor.fetch/github.com/foo/bar@v1.2.3
|
|
# p = github.com/foo/bar@v1.2.3
|
|
# repo = github.com/foo/bar
|
|
# version = v1.2.3
|
|
|
|
p = destsuffix[len(default_destsuffix)+1:]
|
|
repo, version = p.split('@')
|
|
|
|
module_path = fetcher.ud[url].parm.get('go_module_path')
|
|
|
|
subdir = fetcher.ud[url].parm.get('go_subdir')
|
|
subdir = None if not subdir else subdir
|
|
|
|
pathMajor = fetcher.ud[url].parm.get('go_pathmajor')
|
|
pathMajor = None if not pathMajor else pathMajor.strip('/')
|
|
|
|
if not (repo, version) in modules:
|
|
modules[(repo, version)] = {
|
|
"repo_path": os.path.join(import_dir, p),
|
|
"module_path": module_path,
|
|
"subdir": subdir,
|
|
"pathMajor": pathMajor }
|
|
|
|
for module_key, module in modules.items():
|
|
|
|
# only take the version which is explicitly listed
|
|
# as a dependency in the go.mod
|
|
module_path = module['module_path']
|
|
rootdir = module['repo_path']
|
|
subdir = module['subdir']
|
|
pathMajor = module['pathMajor']
|
|
|
|
src = rootdir
|
|
|
|
if subdir:
|
|
src = os.path.join(rootdir, subdir)
|
|
|
|
# If the module is released at major version 2 or higher, the module
|
|
# path must end with a major version suffix like /v2.
|
|
# This may or may not be part of the subdirectory name
|
|
#
|
|
# https://go.dev/ref/mod#modules-overview
|
|
if pathMajor:
|
|
tmp = os.path.join(src, pathMajor)
|
|
# source directory including major version path may or may not exist
|
|
if os.path.exists(tmp):
|
|
src = tmp
|
|
|
|
dst = os.path.join(vendor_dir, module_path)
|
|
|
|
bb.debug(1, "cp %s --> %s" % (src, dst))
|
|
shutil.copytree(src, dst, symlinks=True, dirs_exist_ok=True, \
|
|
ignore=shutil.ignore_patterns(".git", \
|
|
"vendor", \
|
|
"*._test.go"))
|
|
|
|
# If the root directory has a LICENSE file but not the subdir
|
|
# we copy the root license to the sub module since the license
|
|
# applies to all modules in the repository
|
|
# see https://go.dev/ref/mod#vcs-license
|
|
if subdir:
|
|
rootdirLicese = os.path.join(rootdir, "LICENSE")
|
|
subdirLicense = os.path.join(src, "LICENSE")
|
|
|
|
if not os.path.exists(subdir) and \
|
|
os.path.exists(rootdirLicese):
|
|
shutil.copy2(rootdirLicese, subdirLicense)
|
|
|
|
# Copy vendor manifest
|
|
modules_txt_src = os.path.join(d.getVar('UNPACKDIR'), "modules.txt")
|
|
bb.debug(1, "cp %s --> %s" % (modules_txt_src, vendor_dir))
|
|
shutil.copy2(modules_txt_src, vendor_dir)
|
|
|
|
# Clean up vendor dir
|
|
# We only require the modules in the modules_txt file
|
|
fetched_paths = set([os.path.relpath(x[0], vendor_dir) for x in os.walk(vendor_dir)])
|
|
|
|
# Remove toplevel dir
|
|
fetched_paths.remove('.')
|
|
|
|
vendored_paths = set()
|
|
replaced_paths = dict()
|
|
with open(modules_txt_src) as f:
|
|
for line in f:
|
|
if not line.startswith("#"):
|
|
line = line.strip()
|
|
vendored_paths.add(line)
|
|
|
|
# Add toplevel dirs into vendored dir, as we want to keep them
|
|
topdir = os.path.dirname(line)
|
|
while len(topdir):
|
|
if not topdir in vendored_paths:
|
|
vendored_paths.add(topdir)
|
|
|
|
topdir = os.path.dirname(topdir)
|
|
else:
|
|
replaced_module = line.split("=>")
|
|
if len(replaced_module) > 1:
|
|
# This module has been replaced, use a local path
|
|
# we parse the line that has a pattern "# module-name [module-version] => local-path
|
|
actual_path = replaced_module[1].strip()
|
|
vendored_name = replaced_module[0].split()[1]
|
|
bb.debug(1, "added vendored name %s for actual path %s" % (vendored_name, actual_path))
|
|
replaced_paths[vendored_name] = actual_path
|
|
|
|
for path in fetched_paths:
|
|
if path not in vendored_paths:
|
|
realpath = os.path.join(vendor_dir, path)
|
|
if os.path.exists(realpath):
|
|
shutil.rmtree(realpath)
|
|
|
|
for vendored_name, replaced_path in replaced_paths.items():
|
|
symlink_target = os.path.join(source_dir, *['src', go_import, replaced_path])
|
|
symlink_name = os.path.join(vendor_dir, vendored_name)
|
|
relative_symlink_target = os.path.relpath(symlink_target, os.path.dirname(symlink_name))
|
|
bb.debug(1, "vendored name %s, symlink name %s" % (vendored_name, symlink_name))
|
|
|
|
os.makedirs(os.path.dirname(symlink_name), exist_ok=True)
|
|
os.symlink(relative_symlink_target, symlink_name)
|
|
|
|
# Create a symlink to the actual directory
|
|
relative_vendor_dir = os.path.relpath(vendor_dir, os.path.dirname(linkname))
|
|
os.symlink(relative_vendor_dir, linkname)
|
|
}
|
|
|
|
addtask go_vendor before do_patch after do_unpack
|