Complete Yocto mirror with license table for TQMa6UL (2038-compliance)

- 264 license table entries with exact download URLs (224/264 resolved)
- Complete sources/ directory with all BitBake recipes
- Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl)
- Full traceability for Softwarefreigabeantrag
- GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4
- License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
This commit is contained in:
Siggi (OpenClaw Agent)
2026-03-01 20:58:18 +00:00
commit 16accb6b24
15086 changed files with 1292356 additions and 0 deletions

View File

@@ -0,0 +1,515 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
import glob
import re
import shutil
import subprocess
from oe.package_manager import *
class OpkgIndexer(Indexer):
def write_index(self):
arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
"SDK_PACKAGE_ARCHS",
]
opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
opkg_index_cmd_extra_params = self.d.getVar('OPKG_MAKE_INDEX_EXTRA_PARAMS') or ""
if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
else:
signer = None
if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
open(os.path.join(self.deploy_dir, "Packages"), "w").close()
index_cmds = set()
index_sign_files = set()
for arch_var in arch_vars:
archs = self.d.getVar(arch_var)
if archs is None:
continue
for arch in archs.split():
pkgs_dir = os.path.join(self.deploy_dir, arch)
pkgs_file = os.path.join(pkgs_dir, "Packages")
if not os.path.isdir(pkgs_dir):
continue
if not os.path.exists(pkgs_file):
open(pkgs_file, "w").close()
index_cmds.add('%s --checksum md5 --checksum sha256 -r %s -p %s -m %s %s' %
(opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir, opkg_index_cmd_extra_params))
index_sign_files.add(pkgs_file)
if len(index_cmds) == 0:
bb.note("There are no packages in %s!" % self.deploy_dir)
return
oe.utils.multiprocess_launch(create_index, index_cmds, self.d)
if signer:
feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
is_ascii_sig = (feed_sig_type.upper() != "BIN")
for f in index_sign_files:
signer.detach_sign(f,
self.d.getVar('PACKAGE_FEED_GPG_NAME'),
self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
armor=is_ascii_sig)
class PMPkgsList(PkgsList):
def __init__(self, d, rootfs_dir):
super(PMPkgsList, self).__init__(d, rootfs_dir)
config_file = d.getVar("IPKGCONF_TARGET")
self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
self.opkg_args += self.d.getVar("OPKG_ARGS")
def list_pkgs(self, format=None):
cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
# opkg returns success even when it printed some
# "Collected errors:" report to stderr. Mixing stderr into
# stdout then leads to random failures later on when
# parsing the output. To avoid this we need to collect both
# output streams separately and check for empty stderr.
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
cmd_output, cmd_stderr = p.communicate()
cmd_output = cmd_output.decode("utf-8")
cmd_stderr = cmd_stderr.decode("utf-8")
if p.returncode or cmd_stderr:
bb.fatal("Cannot get the installed packages list. Command '%s' "
"returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
return opkg_query(cmd_output)
class OpkgDpkgPM(PackageManager):
def __init__(self, d, target_rootfs):
"""
This is an abstract class. Do not instantiate this directly.
"""
super(OpkgDpkgPM, self).__init__(d, target_rootfs)
def package_info(self, pkg, cmd):
"""
Returns a dictionary with the package info.
This method extracts the common parts for Opkg and Dpkg
"""
proc = subprocess.run(cmd, capture_output=True, encoding="utf-8", shell=True)
if proc.returncode:
bb.fatal("Unable to list available packages. Command '%s' "
"returned %d:\n%s" % (cmd, proc.returncode, proc.stderr))
elif proc.stderr:
bb.note("Command '%s' returned stderr: %s" % (cmd, proc.stderr))
return opkg_query(proc.stdout)
def extract(self, pkg, pkg_info):
"""
Returns the path to a tmpdir where resides the contents of a package.
Deleting the tmpdir is responsability of the caller.
This method extracts the common parts for Opkg and Dpkg
"""
ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
pkg_path = pkg_info[pkg]["filepath"]
if not os.path.isfile(pkg_path):
bb.fatal("Unable to extract package for '%s'."
"File %s doesn't exists" % (pkg, pkg_path))
tmp_dir = tempfile.mkdtemp()
current_dir = os.getcwd()
os.chdir(tmp_dir)
try:
cmd = [ar_cmd, 'x', pkg_path]
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
data_tar = glob.glob("data.tar.*")
if len(data_tar) != 1:
bb.fatal("Unable to extract %s package. Failed to identify "
"data tarball (found tarballs '%s').",
pkg_path, data_tar)
data_tar = data_tar[0]
cmd = [tar_cmd, 'xf', data_tar]
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
bb.utils.remove(tmp_dir, recurse=True)
bb.fatal("Unable to extract %s package. Command '%s' "
"returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
except OSError as e:
bb.utils.remove(tmp_dir, recurse=True)
bb.fatal("Unable to extract %s package. Command '%s' "
"returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
bb.utils.remove(os.path.join(tmp_dir, data_tar))
os.chdir(current_dir)
return tmp_dir
def _handle_intercept_failure(self, registered_pkgs):
self.mark_packages("unpacked", registered_pkgs.split())
class OpkgPM(OpkgDpkgPM):
def __init__(self, d, target_rootfs, config_file, archs, task_name='target', ipk_repo_workdir="oe-rootfs-repo", filterbydependencies=True, prepare_index=True):
super(OpkgPM, self).__init__(d, target_rootfs)
self.config_file = config_file
self.pkg_archs = archs
self.task_name = task_name
self.deploy_dir = oe.path.join(self.d.getVar('WORKDIR'), ipk_repo_workdir)
self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
self.opkg_args = "--volatile-cache -f %s -t %s -o %s " % (self.config_file, self.d.expand('${T}/ipktemp/') ,target_rootfs)
self.opkg_args += self.d.getVar("OPKG_ARGS")
if prepare_index:
create_packages_dir(self.d, self.deploy_dir, d.getVar("DEPLOY_DIR_IPK"), "package_write_ipk", filterbydependencies)
self.opkg_dir = oe.path.join(target_rootfs, self.d.getVar('OPKGLIBDIR'), "opkg")
bb.utils.mkdirhier(self.opkg_dir)
self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
if not os.path.exists(self.d.expand('${T}/saved')):
bb.utils.mkdirhier(self.d.expand('${T}/saved'))
self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
if self.from_feeds:
self._create_custom_config()
else:
self._create_config()
self.indexer = OpkgIndexer(self.d, self.deploy_dir)
def mark_packages(self, status_tag, packages=None):
"""
This function will change a package's status in /var/lib/opkg/status file.
If 'packages' is None then the new_status will be applied to all
packages
"""
status_file = os.path.join(self.opkg_dir, "status")
with open(status_file, "r") as sf:
with open(status_file + ".tmp", "w+") as tmp_sf:
if packages is None:
tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
r"Package: \1\n\2Status: \3%s" % status_tag,
sf.read()))
else:
if type(packages).__name__ != "list":
raise TypeError("'packages' should be a list object")
status = sf.read()
for pkg in packages:
status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
status)
tmp_sf.write(status)
bb.utils.rename(status_file + ".tmp", status_file)
def _create_custom_config(self):
bb.note("Building from feeds activated!")
with open(self.config_file, "w+") as config_file:
priority = 1
for arch in self.pkg_archs.split():
config_file.write("arch %s %d\n" % (arch, priority))
priority += 5
for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
feed_match = re.match(r"^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
if feed_match is not None:
feed_name = feed_match.group(1)
feed_uri = feed_match.group(2)
bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
"""
Allow to use package deploy directory contents as quick devel-testing
feed. This creates individual feed configs for each arch subdir of those
specified as compatible for the current machine.
NOTE: Development-helper feature, NOT a full-fledged feed.
"""
if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
for arch in self.pkg_archs.split():
cfg_file_name = oe.path.join(self.target_rootfs,
self.d.getVar("sysconfdir"),
"opkg",
"local-%s-feed.conf" % arch)
with open(cfg_file_name, "w+") as cfg_file:
cfg_file.write("src/gz local-%s %s/%s" %
(arch,
self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
arch))
if self.d.getVar('OPKGLIBDIR') != '/var/lib':
# There is no command line option for this anymore, we need to add
# info_dir and status_file to config file, if OPKGLIBDIR doesn't have
# the default value of "/var/lib" as defined in opkg:
# libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
# libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
# libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
def _create_config(self):
with open(self.config_file, "w+") as config_file:
priority = 1
for arch in self.pkg_archs.split():
config_file.write("arch %s %d\n" % (arch, priority))
priority += 5
config_file.write("src oe file:%s\n" % self.deploy_dir)
for arch in self.pkg_archs.split():
pkgs_dir = os.path.join(self.deploy_dir, arch)
if os.path.isdir(pkgs_dir):
config_file.write("src oe-%s file:%s\n" %
(arch, pkgs_dir))
if self.d.getVar('OPKGLIBDIR') != '/var/lib':
# There is no command line option for this anymore, we need to add
# info_dir and status_file to config file, if OPKGLIBDIR doesn't have
# the default value of "/var/lib" as defined in opkg:
# libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
# libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
# libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
if feed_uris == "":
return
rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
% self.target_rootfs)
os.makedirs('%s/etc/opkg' % self.target_rootfs, exist_ok=True)
feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
with open(rootfs_config, "w+") as config_file:
uri_iterator = 0
for uri in feed_uris:
if archs:
for arch in archs:
if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
continue
bb.note('Adding opkg feed url-%s-%d (%s)' %
(arch, uri_iterator, uri))
config_file.write("src/gz uri-%s-%d %s/%s\n" %
(arch, uri_iterator, uri, arch))
else:
bb.note('Adding opkg feed url-%d (%s)' %
(uri_iterator, uri))
config_file.write("src/gz uri-%d %s\n" %
(uri_iterator, uri))
uri_iterator += 1
def update(self):
self.deploy_dir_lock()
cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
try:
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
self.deploy_dir_unlock()
bb.fatal("Unable to update the package index files. Command '%s' "
"returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
self.deploy_dir_unlock()
def install(self, pkgs, attempt_only=False, hard_depends_only=False):
if not pkgs:
return
cmd = "%s %s" % (self.opkg_cmd, self.opkg_args)
for exclude in (self.d.getVar("PACKAGE_EXCLUDE") or "").split():
cmd += " --add-exclude %s" % exclude
for bad_recommendation in (self.d.getVar("BAD_RECOMMENDATIONS") or "").split():
cmd += " --add-ignore-recommends %s" % bad_recommendation
if hard_depends_only:
cmd += " --no-install-recommends"
cmd += " install "
cmd += " ".join(pkgs)
os.environ['D'] = self.target_rootfs
os.environ['OFFLINE_ROOT'] = self.target_rootfs
os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
os.environ['INTERCEPT_DIR'] = self.intercepts_dir
os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
try:
bb.note("Installing the following packages: %s" % ' '.join(pkgs))
bb.note(cmd)
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
bb.note(output)
failed_pkgs = []
for line in output.split('\n'):
if line.endswith("configuration required on target."):
bb.warn(line)
failed_pkgs.append(line.split(".")[0])
if failed_pkgs:
failed_postinsts_abort(failed_pkgs, self.d.expand("${T}/log.do_${BB_CURRENTTASK}"))
except subprocess.CalledProcessError as e:
(bb.fatal, bb.warn)[attempt_only]("Unable to install packages. "
"Command '%s' returned %d:\n%s" %
(cmd, e.returncode, e.output.decode("utf-8")))
def remove(self, pkgs, with_dependencies=True):
if not pkgs:
return
if with_dependencies:
cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
(self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
else:
cmd = "%s %s --force-depends remove %s" % \
(self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
try:
bb.note(cmd)
output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
bb.note(output)
except subprocess.CalledProcessError as e:
bb.fatal("Unable to remove packages. Command '%s' "
"returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
def write_index(self):
self.deploy_dir_lock()
result = self.indexer.write_index()
self.deploy_dir_unlock()
if result is not None:
bb.fatal(result)
def remove_packaging_data(self):
cachedir = oe.path.join(self.target_rootfs, self.d.getVar("localstatedir"), "cache", "opkg")
bb.utils.remove(self.opkg_dir, True)
bb.utils.remove(cachedir, True)
def remove_lists(self):
if not self.from_feeds:
bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
def list_installed(self):
return PMPkgsList(self.d, self.target_rootfs).list_pkgs()
def dummy_install(self, pkgs):
"""
The following function dummy installs pkgs and returns the log of output.
"""
if len(pkgs) == 0:
return
# Create an temp dir as opkg root for dummy installation
temp_rootfs = self.d.expand('${T}/opkg')
opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
if opkg_lib_dir[0] == "/":
opkg_lib_dir = opkg_lib_dir[1:]
temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
bb.utils.mkdirhier(temp_opkg_dir)
opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
opkg_args += self.d.getVar("OPKG_ARGS")
cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
try:
subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError as e:
bb.fatal("Unable to update. Command '%s' "
"returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
# Dummy installation
cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
opkg_args,
' '.join(pkgs))
proc = subprocess.run(cmd, capture_output=True, encoding="utf-8", shell=True)
if proc.returncode:
bb.fatal("Unable to dummy install packages. Command '%s' "
"returned %d:\n%s" % (cmd, proc.returncode, proc.stderr))
elif proc.stderr:
bb.note("Command '%s' returned stderr: %s" % (cmd, proc.stderr))
bb.utils.remove(temp_rootfs, True)
return proc.stdout
def backup_packaging_data(self):
# Save the opkglib for increment ipk image generation
if os.path.exists(self.saved_opkg_dir):
bb.utils.remove(self.saved_opkg_dir, True)
shutil.copytree(self.opkg_dir,
self.saved_opkg_dir,
symlinks=True)
def recover_packaging_data(self):
# Move the opkglib back
if os.path.exists(self.saved_opkg_dir):
if os.path.exists(self.opkg_dir):
bb.utils.remove(self.opkg_dir, True)
bb.note('Recover packaging data')
shutil.copytree(self.saved_opkg_dir,
self.opkg_dir,
symlinks=True)
def package_info(self, pkg):
"""
Returns a dictionary with the package info.
"""
cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
pkg_arch = pkg_info[pkg]["arch"]
pkg_filename = pkg_info[pkg]["filename"]
pkg_info[pkg]["filepath"] = \
os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
return pkg_info
def extract(self, pkg):
"""
Returns the path to a tmpdir where resides the contents of a package.
Deleting the tmpdir is responsability of the caller.
"""
pkg_info = self.package_info(pkg)
if not pkg_info:
bb.fatal("Unable to get information for package '%s' while "
"trying to extract the package." % pkg)
return super(OpkgPM, self).extract(pkg, pkg_info)

View File

@@ -0,0 +1,76 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
from oe.manifest import Manifest
import re
class PkgManifest(Manifest):
"""
Returns a dictionary object with mip and mlp packages.
"""
def _split_multilib(self, pkg_list):
pkgs = dict()
for pkg in pkg_list.split():
pkg_type = self.PKG_TYPE_MUST_INSTALL
ml_variants = self.d.getVar('MULTILIB_VARIANTS').split()
for ml_variant in ml_variants:
if pkg.startswith(ml_variant + '-'):
pkg_type = self.PKG_TYPE_MULTILIB
if not pkg_type in pkgs:
pkgs[pkg_type] = pkg
else:
pkgs[pkg_type] += " " + pkg
return pkgs
def create_initial(self):
pkgs = dict()
with open(self.initial_manifest, "w+") as manifest:
manifest.write(self.initial_manifest_file_header)
for var in self.var_maps[self.manifest_type]:
if var in self.vars_to_split:
split_pkgs = self._split_multilib(self.d.getVar(var))
if split_pkgs is not None:
pkgs = dict(list(pkgs.items()) + list(split_pkgs.items()))
else:
pkg_list = self.d.getVar(var)
if pkg_list is not None:
pkgs[self.var_maps[self.manifest_type][var]] = self.d.getVar(var)
for pkg_type in sorted(pkgs):
for pkg in sorted(pkgs[pkg_type].split()):
manifest.write("%s,%s\n" % (pkg_type, pkg))
def create_final(self):
pass
def create_full(self, pm):
if not os.path.exists(self.initial_manifest):
self.create_initial()
initial_manifest = self.parse_initial_manifest()
pkgs_to_install = list()
for pkg_type in initial_manifest:
pkgs_to_install += initial_manifest[pkg_type]
if len(pkgs_to_install) == 0:
return
output = pm.dummy_install(pkgs_to_install)
with open(self.full_manifest, 'w+') as manifest:
pkg_re = re.compile('^Installing ([^ ]+) [^ ].*')
for line in set(output.split('\n')):
m = pkg_re.match(line)
if m:
manifest.write(m.group(1) + '\n')
return

View File

@@ -0,0 +1,352 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
import re
import filecmp
import shutil
from oe.rootfs import Rootfs
from oe.manifest import Manifest
from oe.utils import execute_pre_post_process
from oe.package_manager.ipk.manifest import PkgManifest
from oe.package_manager.ipk import OpkgPM
class DpkgOpkgRootfs(Rootfs):
def __init__(self, d, progress_reporter=None, logcatcher=None):
super(DpkgOpkgRootfs, self).__init__(d, progress_reporter, logcatcher)
def _get_pkgs_postinsts(self, status_file):
def _get_pkg_depends_list(pkg_depends):
pkg_depends_list = []
# filter version requirements like libc (>= 1.1)
for dep in pkg_depends.split(', '):
m_dep = re.match(r"^(.*) \(.*\)$", dep)
if m_dep:
dep = m_dep.group(1)
pkg_depends_list.append(dep)
return pkg_depends_list
pkgs = {}
pkg_name = ""
pkg_status_match = False
pkg_depends = ""
with open(status_file) as status:
data = status.read()
status.close()
for line in data.split('\n'):
m_pkg = re.match(r"^Package: (.*)", line)
m_status = re.match(r"^Status:.*unpacked", line)
m_depends = re.match(r"^Depends: (.*)", line)
#Only one of m_pkg, m_status or m_depends is not None at time
#If m_pkg is not None, we started a new package
if m_pkg is not None:
#Get Package name
pkg_name = m_pkg.group(1)
#Make sure we reset other variables
pkg_status_match = False
pkg_depends = ""
elif m_status is not None:
#New status matched
pkg_status_match = True
elif m_depends is not None:
#New depends macthed
pkg_depends = m_depends.group(1)
else:
pass
#Now check if we can process package depends and postinst
if "" != pkg_name and pkg_status_match:
pkgs[pkg_name] = _get_pkg_depends_list(pkg_depends)
else:
#Not enough information
pass
# remove package dependencies not in postinsts
pkg_names = list(pkgs.keys())
for pkg_name in pkg_names:
deps = pkgs[pkg_name][:]
for d in deps:
if d not in pkg_names:
pkgs[pkg_name].remove(d)
return pkgs
def _get_delayed_postinsts_common(self, status_file):
def _dep_resolve(graph, node, resolved, seen):
seen.append(node)
for edge in graph[node]:
if edge not in resolved:
if edge in seen:
raise RuntimeError("Packages %s and %s have " \
"a circular dependency in postinsts scripts." \
% (node, edge))
_dep_resolve(graph, edge, resolved, seen)
resolved.append(node)
pkg_list = []
pkgs = None
if not self.d.getVar('PACKAGE_INSTALL').strip():
bb.note("Building empty image")
else:
pkgs = self._get_pkgs_postinsts(status_file)
if pkgs:
root = "__packagegroup_postinst__"
pkgs[root] = list(pkgs.keys())
_dep_resolve(pkgs, root, pkg_list, [])
pkg_list.remove(root)
if len(pkg_list) == 0:
return None
return pkg_list
def _save_postinsts_common(self, dst_postinst_dir, src_postinst_dir):
if bb.utils.contains("IMAGE_FEATURES", "package-management",
True, False, self.d):
return
num = 0
for p in self._get_delayed_postinsts():
bb.utils.mkdirhier(dst_postinst_dir)
if os.path.exists(os.path.join(src_postinst_dir, p + ".postinst")):
shutil.copy(os.path.join(src_postinst_dir, p + ".postinst"),
os.path.join(dst_postinst_dir, "%03d-%s" % (num, p)))
num += 1
class PkgRootfs(DpkgOpkgRootfs):
def __init__(self, d, manifest_dir, progress_reporter=None, logcatcher=None):
super(PkgRootfs, self).__init__(d, progress_reporter, logcatcher)
self.log_check_regex = '(exit 1|Collected errors)'
self.manifest = PkgManifest(d, manifest_dir)
self.opkg_conf = self.d.getVar("IPKGCONF_TARGET")
self.pkg_archs = self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS")
self.inc_opkg_image_gen = self.d.getVar('INC_IPK_IMAGE_GEN') or ""
if self._remove_old_rootfs():
bb.utils.remove(self.image_rootfs, True)
self.pm = OpkgPM(d,
self.image_rootfs,
self.opkg_conf,
self.pkg_archs)
else:
self.pm = OpkgPM(d,
self.image_rootfs,
self.opkg_conf,
self.pkg_archs)
self.pm.recover_packaging_data()
bb.utils.remove(self.d.getVar('MULTILIB_TEMP_ROOTFS'), True)
'''
Compare two files with the same key twice to see if they are equal.
If they are not equal, it means they are duplicated and come from
different packages.
'''
def _file_equal(self, key, f1, f2):
if filecmp.cmp(f1, f2):
return True
# Not equal
return False
"""
This function was reused from the old implementation.
See commit: "image.bbclass: Added variables for multilib support." by
Lianhao Lu.
"""
def _multilib_sanity_test(self, dirs):
allow_replace = "|".join((self.d.getVar("MULTILIBRE_ALLOW_REP") or "").split())
if allow_replace is None:
allow_replace = ""
allow_rep = re.compile(re.sub(r"\|$", r"", allow_replace))
error_prompt = "Multilib check error:"
files = {}
for dir in dirs:
for root, subfolders, subfiles in os.walk(dir):
for file in subfiles:
item = os.path.join(root, file)
key = str(os.path.join("/", os.path.relpath(item, dir)))
valid = True
if key in files:
#check whether the file is allow to replace
if allow_rep.match(key):
valid = True
else:
if os.path.exists(files[key]) and \
os.path.exists(item) and \
not self._file_equal(key, files[key], item):
valid = False
bb.fatal("%s duplicate files %s %s is not the same\n" %
(error_prompt, item, files[key]))
#pass the check, add to list
if valid:
files[key] = item
def _multilib_test_install(self, pkgs):
ml_temp = self.d.getVar("MULTILIB_TEMP_ROOTFS")
bb.utils.mkdirhier(ml_temp)
dirs = [self.image_rootfs]
for variant in self.d.getVar("MULTILIB_VARIANTS").split():
ml_target_rootfs = os.path.join(ml_temp, variant)
bb.utils.remove(ml_target_rootfs, True)
ml_opkg_conf = os.path.join(ml_temp,
variant + "-" + os.path.basename(self.opkg_conf))
ml_pm = OpkgPM(self.d, ml_target_rootfs, ml_opkg_conf, self.pkg_archs, prepare_index=False)
ml_pm.update()
ml_pm.install(pkgs)
dirs.append(ml_target_rootfs)
self._multilib_sanity_test(dirs)
'''
While ipk incremental image generation is enabled, it will remove the
unneeded pkgs by comparing the old full manifest in previous existing
image and the new full manifest in the current image.
'''
def _remove_extra_packages(self, pkgs_initial_install):
if self.inc_opkg_image_gen == "1":
# Parse full manifest in previous existing image creation session
old_full_manifest = self.manifest.parse_full_manifest()
# Create full manifest for the current image session, the old one
# will be replaced by the new one.
self.manifest.create_full(self.pm)
# Parse full manifest in current image creation session
new_full_manifest = self.manifest.parse_full_manifest()
pkg_to_remove = list()
for pkg in old_full_manifest:
if pkg not in new_full_manifest:
pkg_to_remove.append(pkg)
if pkg_to_remove != []:
bb.note('decremental removed: %s' % ' '.join(pkg_to_remove))
self.pm.remove(pkg_to_remove)
'''
Compare with previous existing image creation, if some conditions
triggered, the previous old image should be removed.
The conditions include any of 'PACKAGE_EXCLUDE, NO_RECOMMENDATIONS
and BAD_RECOMMENDATIONS' has been changed.
'''
def _remove_old_rootfs(self):
if self.inc_opkg_image_gen != "1":
return True
vars_list_file = self.d.expand('${T}/vars_list')
old_vars_list = ""
if os.path.exists(vars_list_file):
old_vars_list = open(vars_list_file, 'r+').read()
new_vars_list = '%s:%s:%s\n' % \
((self.d.getVar('BAD_RECOMMENDATIONS') or '').strip(),
(self.d.getVar('NO_RECOMMENDATIONS') or '').strip(),
(self.d.getVar('PACKAGE_EXCLUDE') or '').strip())
open(vars_list_file, 'w+').write(new_vars_list)
if old_vars_list != new_vars_list:
return True
return False
def _create(self):
pkgs_to_install = self.manifest.parse_initial_manifest()
opkg_pre_process_cmds = self.d.getVar('OPKG_PREPROCESS_COMMANDS')
opkg_post_process_cmds = self.d.getVar('OPKG_POSTPROCESS_COMMANDS')
# update PM index files
self.pm.write_index()
execute_pre_post_process(self.d, opkg_pre_process_cmds)
if self.progress_reporter:
self.progress_reporter.next_stage()
# Steps are a bit different in order, skip next
self.progress_reporter.next_stage()
self.pm.update()
if self.progress_reporter:
self.progress_reporter.next_stage()
if self.inc_opkg_image_gen == "1":
self._remove_extra_packages(pkgs_to_install)
if self.progress_reporter:
self.progress_reporter.next_stage()
for pkg_type in self.install_order:
if pkg_type in pkgs_to_install:
# For multilib, we perform a sanity test before final install
# If sanity test fails, it will automatically do a bb.fatal()
# and the installation will stop
if pkg_type == Manifest.PKG_TYPE_MULTILIB:
self._multilib_test_install(pkgs_to_install[pkg_type])
self.pm.install(pkgs_to_install[pkg_type],
[False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
if self.progress_reporter:
self.progress_reporter.next_stage()
self.pm.install_complementary()
if self.progress_reporter:
self.progress_reporter.next_stage()
opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
opkg_dir = os.path.join(opkg_lib_dir, 'opkg')
self._setup_dbg_rootfs([opkg_dir])
execute_pre_post_process(self.d, opkg_post_process_cmds)
if self.inc_opkg_image_gen == "1":
self.pm.backup_packaging_data()
if self.progress_reporter:
self.progress_reporter.next_stage()
@staticmethod
def _depends_list():
return ['IPKGCONF_SDK', 'IPK_FEED_URIS', 'DEPLOY_DIR_IPK', 'IPKGCONF_TARGET', 'INC_IPK_IMAGE_GEN', 'OPKG_ARGS', 'OPKGLIBDIR', 'OPKG_PREPROCESS_COMMANDS', 'OPKG_POSTPROCESS_COMMANDS', 'OPKGLIBDIR']
def _get_delayed_postinsts(self):
status_file = os.path.join(self.image_rootfs,
self.d.getVar('OPKGLIBDIR').strip('/'),
"opkg", "status")
return self._get_delayed_postinsts_common(status_file)
def _save_postinsts(self):
dst_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${sysconfdir}/ipk-postinsts")
src_postinst_dir = self.d.expand("${IMAGE_ROOTFS}${OPKGLIBDIR}/opkg/info")
return self._save_postinsts_common(dst_postinst_dir, src_postinst_dir)
def _log_check(self):
self._log_check_warn()
self._log_check_error()
def _cleanup(self):
self.pm.remove_lists()

View File

@@ -0,0 +1,113 @@
#
# Copyright OpenEmbedded Contributors
#
# SPDX-License-Identifier: GPL-2.0-only
#
import glob
import shutil
from oe.utils import execute_pre_post_process
from oe.sdk import Sdk
from oe.package_manager.ipk.manifest import PkgManifest
from oe.manifest import Manifest
from oe.package_manager.ipk import OpkgPM
class PkgSdk(Sdk):
def __init__(self, d, manifest_dir=None):
super(PkgSdk, self).__init__(d, manifest_dir)
# In sdk_list_installed_packages the call to opkg is hardcoded to
# always use IPKGCONF_TARGET and there's no exposed API to change this
# so simply override IPKGCONF_TARGET to use this separated config file.
ipkgconf_sdk_target = d.getVar("IPKGCONF_SDK_TARGET")
d.setVar("IPKGCONF_TARGET", ipkgconf_sdk_target)
self.target_conf = self.d.getVar("IPKGCONF_TARGET")
self.host_conf = self.d.getVar("IPKGCONF_SDK")
self.target_manifest = PkgManifest(d, self.manifest_dir,
Manifest.MANIFEST_TYPE_SDK_TARGET)
self.host_manifest = PkgManifest(d, self.manifest_dir,
Manifest.MANIFEST_TYPE_SDK_HOST)
ipk_repo_workdir = "oe-sdk-repo"
if "sdk_ext" in d.getVar("BB_RUNTASK"):
ipk_repo_workdir = "oe-sdk-ext-repo"
self.target_pm = OpkgPM(d, self.sdk_target_sysroot, self.target_conf,
self.d.getVar("ALL_MULTILIB_PACKAGE_ARCHS"),
ipk_repo_workdir=ipk_repo_workdir)
self.host_pm = OpkgPM(d, self.sdk_host_sysroot, self.host_conf,
self.d.getVar("SDK_PACKAGE_ARCHS"),
ipk_repo_workdir=ipk_repo_workdir)
def _populate_sysroot(self, pm, manifest):
pkgs_to_install = manifest.parse_initial_manifest()
if (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") != "1":
pm.write_index()
pm.update()
for pkg_type in self.install_order:
if pkg_type in pkgs_to_install:
pm.install(pkgs_to_install[pkg_type],
[False, True][pkg_type == Manifest.PKG_TYPE_ATTEMPT_ONLY])
def _populate(self):
execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_PRE_TARGET_COMMAND"))
bb.note("Installing TARGET packages")
self._populate_sysroot(self.target_pm, self.target_manifest)
self.target_pm.install_complementary(self.d.getVar('SDKIMAGE_INSTALL_COMPLEMENTARY'))
env_bkp = os.environ.copy()
os.environ['PATH'] = self.d.expand("${COREBASE}/scripts/nativesdk-intercept") + \
os.pathsep + os.environ["PATH"]
self.target_pm.run_intercepts(populate_sdk='target')
os.environ.update(env_bkp)
execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_TARGET_COMMAND"))
if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
self.target_pm.remove_packaging_data()
else:
self.target_pm.remove_lists()
bb.note("Installing NATIVESDK packages")
self._populate_sysroot(self.host_pm, self.host_manifest)
self.install_locales(self.host_pm)
self.host_pm.run_intercepts(populate_sdk='host')
execute_pre_post_process(self.d, self.d.getVar("POPULATE_SDK_POST_HOST_COMMAND"))
if not bb.utils.contains("SDKIMAGE_FEATURES", "package-management", True, False, self.d):
self.host_pm.remove_packaging_data()
else:
self.host_pm.remove_lists()
target_sysconfdir = os.path.join(self.sdk_target_sysroot, self.sysconfdir)
host_sysconfdir = os.path.join(self.sdk_host_sysroot, self.sysconfdir)
self.mkdirhier(target_sysconfdir)
shutil.copy(self.target_conf, target_sysconfdir)
os.chmod(os.path.join(target_sysconfdir,
os.path.basename(self.target_conf)), 0o644)
self.mkdirhier(host_sysconfdir)
shutil.copy(self.host_conf, host_sysconfdir)
os.chmod(os.path.join(host_sysconfdir,
os.path.basename(self.host_conf)), 0o644)
native_opkg_state_dir = os.path.join(self.sdk_output, self.sdk_native_path,
self.d.getVar('localstatedir_nativesdk').strip('/'),
"lib", "opkg")
self.mkdirhier(native_opkg_state_dir)
for f in glob.glob(os.path.join(self.sdk_output, "var", "lib", "opkg", "*")):
self.movefile(f, native_opkg_state_dir)
self.remove(os.path.join(self.sdk_output, "var"), True)