# -*- coding: UTF-8 -*- import os import re import sys import platform import lib from log import * DISTDIR_DEFAULT = 'distribution' BUILD_PREFIX_DEFAULT = '/var/tmp/portage' BUILD_TEMPDIR_DEFAULT = '/var/tmp/prepare' PARSE_EBUILD_FILENAME = re.compile("^.*/([^/]+)/([^/]+)/([^/]+)\.ebuild$") # collect source package configuration ######################## # config variables used: # kernels: version of source kernel ebuild package to use [default='latest']; eg: 5.15.41, 5.15, latest # distdir: name of distribution base directory [defalt='distribution'] # package: name of kernel package configuration [mandatory] # packages..pkg: name of source kernel ebuild package for this package configuration [mandatory] # ebuild.cleanup: boolean flag indicating request to clean up temp files [default=True] # ebuild.prefix: name of portage build base directory [default='/var/tmp/portage'] # ebuild.tempdir: name of ktool build base directory [default='/var/tmp/prepare'] # ebuild.format: name of package format configuration to use [mandatory] # formats..extension: extension to use for this format configuration [mandatory] # formats..archive: archive creation command to use for format configuration [mandatory]; variables: SRCDIR=source directory, DESTDIR=target directory, DESTFILENAME=target filename # formats..extract: archive extraction command to use for format configuration [mandatory]; variables: SRCFILE=source file, DESTDIR=target directory # context variables set up: # arch: runtime architecture (x86, x64) # pkg_name: name of kernel package configuration to use (copied from config variable 'package'); eg: mygentoo # package_to_check: name and version of source kernel ebuild package to use (constructed from config variables 'packages..pkg' and 'kernels'); eg: sys-kernel/mygentoo-sources-5.15 # build_prefix: name of portage build base directory (copied from config variable 'ebuild.prefix'); eg: /var/tmp/portage # build_tempdir: name of ktool build base directory (copied from config variable 'ebuild.tempdir'); eg: /var/tmp/prepare # distdir: name of distribution base directory (copied from config variable 'distdir'); eg: distribution # build_clean: boolean flag indicating request to clean up temp files (copied from config variable 'ebuild.cleanup'); eg: True # build_source_format: name of package format configuration to use (copied from config variable 'ebuild.format'); eg: tarxz # build_source_extension: extension to use for package format (copied from config variable 'formats..extension'); eg: tar.xz # build_source_archive: archive creation command to use for package format (copied from config variable 'formats..archive'); eg: tar cJfC # build_source_extract: archive creation command to use for package format (copied from config variable 'formats..extract'); eg: tar xJfC ######################## def get_source_package_config(config, context): arch = platform.machine() if arch == 'i686': arch = 'x86' elif arch == 'x86_64': arch = 'x64' else: sys.stdout.write("Unsupported cpu architecture: %s\n" % (arch)) return 2 context['arch'] = arch config_pkg_name = config.get_config_param('package', mandatory=True) if config_pkg_name == None: return 3 context['pkg_name'] = config_pkg_name config_pkg_id = 'packages.%s.pkg' % (config_pkg_name) package_name = config.get_config_param(config_pkg_id, mandatory=True) if package_name == None: return 3 build_clean = config.get_config_param('ebuild.cleanup', default=True, type='boolean') context['build_clean'] = build_clean version_wanted = config.get_config_param('kernels', default='latest') if version_wanted == 'latest': version_wanted = '' package_to_check = package_name if version_wanted == '' else '%s-%s*' % (package_name, version_wanted) build_prefix = config.get_config_param('ebuild.prefix', default=BUILD_PREFIX_DEFAULT) build_tempdir = config.get_config_param('ebuild.tempdir', default=BUILD_TEMPDIR_DEFAULT) distdir = config.get_config_param('distdir', default=DISTDIR_DEFAULT) context['package_to_check'] = package_to_check context['build_prefix'] = build_prefix context['build_tempdir'] = build_tempdir context['distdir'] = distdir build_source_format = config.get_config_param('ebuild.format', mandatory=True) if build_source_format == None: return 3 build_source_extension = config.get_config_param('formats.%s.extension' % (build_source_format), mandatory=True) if build_source_extension == None: return 3 build_source_archive = config.get_config_param('formats.%s.archive' % (build_source_format), mandatory=True) if build_source_archive == None: return 3 build_source_extract = config.get_config_param('formats.%s.extract' % (build_source_format), mandatory=True) if build_source_extract == None: return 3 context['build_source_format'] = build_source_format context['build_source_extension'] = build_source_extension context['build_source_archive'] = build_source_archive context['build_source_extract'] = build_source_extract for key in 'arch', 'pkg_name', 'build_prefix', 'build_tempdir', 'distdir', 'build_source_format', 'build_source_extension', 'build_source_archive', 'build_source_extract': sys.stdout.write("%s: %s\n" % (key, context[key])) return 0 # determine source ebuild and version info ######################## # context variables used: # package_to_check (get_source_package_config) # build_prefix (get_source_package_config) # context variables set up: # found_ebuild_filename: name of ebuild file found using 'equery which' based on context variable 'package_to_check'; eg: /var/db/repos/myoverlay/sys-kernel/mygentoo-sources/mygentoo-sources-5.15.41.ebuild # found_package_group: package group parsed from ebuild file directory path ...///-.ebuild; eg: sys-kernel # found_package_name: package name parsed from ebuild file directory path ...///-.ebuild; eg: mygentoo-sources # found_version: package version parsed from ebuild file name ...///-.ebuild; eg: 5.15.41 # found_package_fullname: package full atom identifier reconstructed from package group, package name, package version; eg: sys-kernel/mygentoo-sources-5.15.41 # build_image_dir: name of image directory within portage package build directory (constructed from context variable 'build_prefix' and package full atom identifier); eg: /var/tmp/portage/sys-kernel/mygentoo-sources-5.15.41/image # build_env_file: name of env file within portage package build directory (constructed from context variable 'build_prefix' and package full atom identifier); eg: /var/tmp/portage/sys-kernel/mygentoo-sources-5.15.41/temp/environment ######################## def get_source_ebuild_info(config, context): result = lib.run_command(config, "get ebuild file for package %s" % (context['package_to_check']), ['equery', 'which', context['package_to_check']]) if result == None: return 3 found_ebuild_filename = result.strip() m = PARSE_EBUILD_FILENAME.match(found_ebuild_filename) if not m: sys.stdout.write("Could not parse ebuild file name: %s\n" % (found_ebuild_filename)) return 3 found_package_group = m.group(1) found_package_name = m.group(2) found_package_plus_version = m.group(3) if not found_package_plus_version.startswith(found_package_name) or found_package_plus_version[len(found_package_name):len(found_package_name)+1] != '-': sys.stdout.write("Inconsistent package name in ebuild file name: %s\n" % (found_ebuild_filename)) return 3 found_version = found_package_plus_version[len(found_package_name) + 1:] found_package_fullname = '%s/%s-%s' % (found_package_group, found_package_name, found_version) sys.stdout.write("Package version found: %s\n" % (found_package_fullname)) context['found_ebuild_filename'] = found_ebuild_filename context['found_package_group'] = found_package_group context['found_package_name'] = found_package_name context['found_version'] = found_version context['found_package_fullname'] = found_package_fullname context['build_image_dir'] = "%s/%s/image" % (context['build_prefix'], found_package_fullname) context['build_env_file'] = "%s/%s/temp/environment" % (context['build_prefix'], found_package_fullname) for key in 'found_ebuild_filename', 'found_package_fullname': sys.stdout.write("%s: %s\n" % (key, context[key])) return 0 # extract build variables ######################## # context variables used: # distdir (get_source_package_config) # build_source_extension (get_source_package_config) # build_image_dir (get_source_ebuild_info) # build_env_file (get_source_ebuild_info) # context variables set up: # kernel_version_string: build variable KV_FULL extracted; eg: 5.15.41-mygentoo # kernel_version_major: build variable KV_MAJOR extracted; eg: 5 # kernel_version_minor: build variable KV_MINOR extracted; eg: 15 # kernel_version_patch: build variable KV_PATCH extracted; eg: 41 # kernel_series: kernel series identifier (constructed from KV_MAJOR and KV_MINOR); eg: 5.15 # kernel_series_distdir: distribution subdirectory for kernel series (constructed from config variable 'distdir' and KV_MAJOR and KV_MINOR); eg: distribution/5.15 # build_kernel_dir: kernel source directory extracted into portage package build directory (constructed from context variables 'build_image_dir' and 'kernel_version_string'); eg: /var/tmp/portage/sys-kernel/mygentoo-sources-5.15.41/image//usr/src/linux-5.15.41-mygentoo # source_archive_filename: kernel source archive filename (constructed from context variables 'kernel_version_string' and 'build_source_extension'); eg: 5.15.41-mygentoo.tar.xz # source_archive_dist: kernel source archive subdirectory (constructed from context variables 'kernel_series_distdir' and 'source_archive_filename'); eg: distribution/5.15/5.15.41-mygentoo.tar.xz ######################## def extract_source_variables(config, context): vars = dict() for variable in ('KV_FULL', 'KV_MAJOR', 'KV_MINOR', 'KV_PATCH'): result = lib.run_command(config, "extract kernel version string from %s" % (context['build_env_file']), ['env', '-i', 'bash', '-c', "source %s && echo \"${%s}\"" % (context['build_env_file'], variable)]) if result == None: return "Could not extract kernel variable %s" % (variable) vars[variable] = result.strip() if vars[variable] == '': return "Could not extract kernel variable %s" % (variable) kernel_version_string = vars['KV_FULL'] context['kernel_version_string'] = kernel_version_string kernel_version_major = vars['KV_MAJOR'] context['kernel_version_major'] = kernel_version_major kernel_version_minor = vars['KV_MINOR'] context['kernel_version_minor'] = kernel_version_minor kernel_version_patch = vars['KV_PATCH'] context['kernel_version_patch'] = kernel_version_patch kernel_series = "%s.%s" % (kernel_version_major, kernel_version_minor) context['kernel_series'] = kernel_series kernel_series_distdir = "%s/%s" % (context['distdir'], kernel_series) context['kernel_series_distdir'] = kernel_series_distdir context['build_kernel_dir'] = "%s/usr/src/linux-%s" % (context['build_image_dir'], kernel_version_string) source_archive_filename = "%s.%s" % (kernel_version_string, context['build_source_extension']) context['source_archive_filename'] = source_archive_filename context['source_archive_dist'] = "%s/%s" % (kernel_series_distdir, source_archive_filename) sys.stdout.write("Kernel version: %s.%s.%s full: %s\n" % (kernel_version_major, kernel_version_minor, kernel_version_patch, kernel_version_string)) for key in 'kernel_version_string', 'kernel_series', 'kernel_series_distdir', 'source_archive_filename': sys.stdout.write("%s: %s\n" % (key, context[key])) return "" # determine if source build is needed ######################## def check_source_build_needed(config, context): sys.stdout.write("Checking build environment file: %s\n" % (context['build_env_file'])) if not os.path.isfile(context['build_env_file']): if config.force: sys.stdout.write("No kernel build environment file found but forced to rebuild source image anyway\n") return True sys.stdout.write("No kernel build environment file found\n") result = lib.run_command(config, "create kernel build environment file from ebuild %s" % (context['found_ebuild_filename']), ['ebuild', context['found_ebuild_filename'], 'clean', 'setup']) if result == None: return True if not os.path.isfile(context['build_env_file']): sys.stdout.write("No kernel build environment file found after setup\n") return True else: if config.force: sys.stdout.write("Existing kernel build environment file found but forced to rebuild source image\n") return True error = extract_source_variables(config, context) if error: sys.stdout.write("%s\n" % (error)) return True if os.path.isfile(context['source_archive_dist']): return False sys.stdout.write("Checking build image location: %s\n" % (context['build_image_dir'])) if not os.path.isdir(context['build_image_dir']): sys.stdout.write("No kernel source image found\n") return True sys.stdout.write("Checking build kernel directory: %s\n" % (context['build_kernel_dir'])) if not os.path.isdir(context['build_kernel_dir']): sys.stdout.write("No kernel directory found\n") return True sys.stdout.write("Existing kernel directory found\n") return False # determine if making build archive is needed ######################## def check_build_archive_needed(config, context): if os.path.isfile(context['source_archive_dist']): if os.path.isdir(context['build_kernel_dir']): try: dest_mtime = os.path.getmtime(context['source_archive_dist']) except OSError as e: return True try: src_mtime = os.path.getmtime(context['build_kernel_dir']) except OSError as e: return True sys.stdout.write("%s vs %s\n" % (src_mtime, dest_mtime)) if src_mtime > dest_mtime: sys.stdout.write("Existing source archive older than built image\n") return True sys.stdout.write("Existing source archive found: %s\n" % (context['source_archive_dist'])) return False return True # create archive for built source ######################## def make_build_source_archive(config, context): sys.stdout.write("Using build kernel directory: %s\n" % (context['build_kernel_dir'])) env = dict(os.environ) env['SRCDIR'] = context['build_kernel_dir'] env['DESTDIR'] = context['kernel_series_distdir'] env['DESTFILENAME'] = context['source_archive_filename'] result = lib.run_command(config, "create archive for kernel source %s" % (context['source_archive_dist']), ['bash', '-c', context['build_source_archive']], env) if result == None: return 3 if not os.path.isfile(context['source_archive_dist']): sys.stdout.write("Kernel source archive file not found after creation\n") return 3 return 0 # build kernel source ######################## def build_source(config, context): result = lib.run_command(config, "create kernel source from ebuild %s" % (context['found_ebuild_filename']), ['ebuild', context['found_ebuild_filename'], 'clean', 'install']) if result == None: return 3 if not os.path.isfile(context['build_env_file']): sys.stdout.write("No kernel build environment file found after ebuild command\n") return 3 error = extract_source_variables(config, context) if error: sys.stdout.write("%s after ebuild command\n" % (error)) return 3 if not os.path.isdir(context['build_image_dir']): sys.stdout.write("No kernel source image found after ebuild command\n") return 3 if not os.path.isdir(context['build_kernel_dir']): sys.stdout.write("No kernel directory found after ebuild command\n") return 3 return 0 # clear kernel source built ######################## def clear_source(config, context): lib.run_command(config, "clear kernel source for ebuild %s" % (context['found_ebuild_filename']), ['ebuild', context['found_ebuild_filename'], 'clean']) return 0 # make source (mksrc command) ######################## def make_source(config): context = dict() rc = get_source_package_config(config, context) if rc: return rc rc = get_source_ebuild_info(config, context) if rc: return rc build = check_source_build_needed(config, context) if build: rc = build_source(config, context) if rc: return rc make = check_build_archive_needed(config, context) if make: rc = make_build_source_archive(config, context) if rc: return rc if context['build_clean']: rc = clear_source(config, context) if rc: return rc return None # prepare source (prepare command) ######################## def prepare_source(config): context = dict() rc = get_source_package_config(config, context) if rc: return rc return None