250 lines
10 KiB
Python
250 lines
10 KiB
Python
# -*- coding: UTF-8 -*-
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import subprocess
|
|
|
|
DISTDIR_DEFAULT = 'distribution'
|
|
BUILD_PREFIX_DEFAULT = '/var/tmp/portage'
|
|
PARSE_EBUILD_FILENAME = re.compile("^.*/([^/]+)/([^/]+)/([^/]+)\.ebuild$")
|
|
|
|
# run command
|
|
def run_command(config, task_name, argv, env = None):
|
|
sys.stdout.write("Running command '%s' to %s\n" % (' '.join(argv), task_name))
|
|
try:
|
|
result = subprocess.run(argv, stdout=subprocess.PIPE, env=env)
|
|
if config.verbose:
|
|
sys.stdout.write("Command output: %s\n" % (result.stdout.decode('utf-8')))
|
|
if result.returncode != 0:
|
|
sys.stdout.write("Failed to %s: exit code %d\n" % (task_name, result.returncode))
|
|
return None
|
|
return result.stdout.decode('utf-8')
|
|
except Exception as e:
|
|
sys.stdout.write("Failed to %s: %s\n" % (task_name, e))
|
|
return None
|
|
|
|
# collect source making configuration
|
|
def get_source_package_config(config, context):
|
|
config_pkg_name = config.get_config_param('package', mandatory=True)
|
|
if config_pkg_name == None:
|
|
return 3
|
|
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)
|
|
distdir = config.get_config_param('distdir', default=DISTDIR_DEFAULT)
|
|
context['package_to_check'] = package_to_check
|
|
context['build_prefix'] = build_prefix
|
|
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
|
|
return 0
|
|
|
|
# determine source ebuild and version info
|
|
def get_source_ebuild_info(config, context):
|
|
result = 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_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)
|
|
return 0
|
|
|
|
# extract build variables
|
|
def extract_source_variables(config, context):
|
|
vars = dict()
|
|
for variable in ('KV_FULL', 'KV_MAJOR', 'KV_MINOR', 'KV_PATCH'):
|
|
result = 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))
|
|
|
|
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 = 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 = 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 = 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):
|
|
run_command(config, "clear kernel source for ebuild %s" % (context['found_ebuild_filename']), ['ebuild', context['found_ebuild_filename'], 'clean'])
|
|
return 0
|
|
|
|
# make source
|
|
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
|
|
def prepare_source(config):
|
|
contetx = dict()
|
|
return None
|