|
|
25cfa1 |
#!/usr/bin/env python3
|
|
|
25cfa1 |
#
|
|
|
25cfa1 |
# This script inspects a given json proving a list of addons, and
|
|
|
25cfa1 |
# creates an addon for each key/value pair matching the given uki, distro and
|
|
|
25cfa1 |
# arch provided in input.
|
|
|
25cfa1 |
#
|
|
|
25cfa1 |
# Usage: python uki_create_addons.py input_json out_dir uki distro arch
|
|
|
25cfa1 |
#
|
|
|
25cfa1 |
# This tool requires the systemd-ukify and systemd-boot packages.
|
|
|
25cfa1 |
#
|
|
|
25cfa1 |
# Addon file
|
|
|
25cfa1 |
#-----------
|
|
|
25cfa1 |
# Each addon terminates with .addon
|
|
|
25cfa1 |
# Each addon contains only two types of lines:
|
|
|
25cfa1 |
# Lines beginning with '#' are description and thus ignored
|
|
|
25cfa1 |
# All other lines are command line to be added.
|
|
|
25cfa1 |
# The name of the end resulting addon is taken from the json hierarchy.
|
|
|
25cfa1 |
# For example, and addon in json['virt']['rhel']['x86_64']['hello.addon'] will
|
|
|
25cfa1 |
# result in an UKI addon file generated in out_dir called
|
|
|
25cfa1 |
# hello-virt.rhel.x86_64.addon.efi
|
|
|
25cfa1 |
#
|
|
|
25cfa1 |
# The common key, present in any sub-dict in the provided json (except the leaf dict)
|
|
|
25cfa1 |
# is used as place for default addons when the same addon is not defined deep
|
|
|
25cfa1 |
# in the hierarchy. For example, if we define test.addon (text: 'test1\n') in
|
|
|
25cfa1 |
# json['common']['test.addon'] = ['test1\n'] and another test.addon (text: test2) in
|
|
|
25cfa1 |
# json['virt']['common']['test.addon'] = ['test2'], any other uki except virt
|
|
|
25cfa1 |
# will have a test.addon.efi with text "test1", and virt will have a
|
|
|
25cfa1 |
# test.addon.efi with "test2"
|
|
|
25cfa1 |
#
|
|
|
25cfa1 |
# sbat.conf
|
|
|
25cfa1 |
#----------
|
|
|
25cfa1 |
# This dict is containing the sbat string for *all* addons being created.
|
|
|
25cfa1 |
# This dict is optional, but when used has to be put in a sub-dict with
|
|
|
25cfa1 |
# { 'sbat' : { 'sbat.conf' : ['your text here'] }}
|
|
|
25cfa1 |
# It follows the same syntax as the addon files, meaning '#' is comment and
|
|
|
25cfa1 |
# the rest is taken as sbat string and feed to ukify.
|
|
|
25cfa1 |
|
|
|
25cfa1 |
import os
|
|
|
25cfa1 |
import sys
|
|
|
25cfa1 |
import json
|
|
|
25cfa1 |
import collections
|
|
|
25cfa1 |
import subprocess
|
|
|
25cfa1 |
|
|
|
25cfa1 |
|
|
|
25cfa1 |
UKIFY_PATH = '/usr/lib/systemd/ukify'
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def usage(err):
|
|
|
25cfa1 |
print(f'Usage: {os.path.basename(__file__)} input_json output_dir uki distro arch')
|
|
|
25cfa1 |
print(f'Error:{err}')
|
|
|
25cfa1 |
sys.exit(1)
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def check_clean_arguments(input_json, out_dir):
|
|
|
25cfa1 |
# Remove end '/'
|
|
|
25cfa1 |
if out_dir[-1:] == '/':
|
|
|
25cfa1 |
out_dir = out_dir[:-1]
|
|
|
25cfa1 |
if not os.path.isfile(input_json):
|
|
|
25cfa1 |
usage(f'input_json {input_json} is not a file, or does not exist!')
|
|
|
25cfa1 |
if not os.path.isdir(out_dir):
|
|
|
25cfa1 |
usage(f'out_dir_dir {out_dir} is not a dir, or does not exist!')
|
|
|
25cfa1 |
return out_dir
|
|
|
25cfa1 |
|
|
|
25cfa1 |
UKICmdlineAddon = collections.namedtuple('UKICmdlineAddon', ['name', 'cmdline'])
|
|
|
25cfa1 |
uki_addons_list = []
|
|
|
25cfa1 |
uki_addons = {}
|
|
|
25cfa1 |
addon_sbat_string = None
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def parse_lines(lines, rstrip=True):
|
|
|
25cfa1 |
cmdline = ''
|
|
|
25cfa1 |
for l in lines:
|
|
|
25cfa1 |
l = l.lstrip()
|
|
|
25cfa1 |
if not l:
|
|
|
25cfa1 |
continue
|
|
|
25cfa1 |
if l[0] == '#':
|
|
|
25cfa1 |
continue
|
|
|
25cfa1 |
# rstrip is used only for addons cmdline, not sbat.conf, as it replaces
|
|
|
25cfa1 |
# return lines with spaces.
|
|
|
25cfa1 |
if rstrip:
|
|
|
25cfa1 |
l = l.rstrip() + ' '
|
|
|
25cfa1 |
cmdline += l
|
|
|
25cfa1 |
if cmdline == '':
|
|
|
25cfa1 |
return ''
|
|
|
25cfa1 |
return cmdline
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def parse_all_addons(in_obj):
|
|
|
25cfa1 |
global addon_sbat_string
|
|
|
25cfa1 |
|
|
|
25cfa1 |
for el in in_obj.keys():
|
|
|
25cfa1 |
# addon found: copy it in our global dict uki_addons
|
|
|
25cfa1 |
if el.endswith('.addon'):
|
|
|
25cfa1 |
uki_addons[el] = in_obj[el]
|
|
|
25cfa1 |
|
|
|
25cfa1 |
if 'sbat' in in_obj and 'sbat.conf' in in_obj['sbat']:
|
|
|
25cfa1 |
# sbat.conf found: override sbat with the most specific one found
|
|
|
25cfa1 |
addon_sbat_string = parse_lines(in_obj['sbat']['sbat.conf'], rstrip=False)
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def recursively_find_addons(in_obj, folder_list):
|
|
|
25cfa1 |
# end of recursion, leaf directory. Search all addons here
|
|
|
25cfa1 |
if len(folder_list) == 0:
|
|
|
25cfa1 |
parse_all_addons(in_obj)
|
|
|
25cfa1 |
return
|
|
|
25cfa1 |
|
|
|
25cfa1 |
# first, check for common folder
|
|
|
25cfa1 |
if 'common' in in_obj:
|
|
|
25cfa1 |
parse_all_addons(in_obj['common'])
|
|
|
25cfa1 |
|
|
|
25cfa1 |
# second, check if there is a match with the searched folder
|
|
|
25cfa1 |
if folder_list[0] in in_obj:
|
|
|
25cfa1 |
folder_next = in_obj[folder_list[0]]
|
|
|
25cfa1 |
folder_list = folder_list[1:]
|
|
|
25cfa1 |
recursively_find_addons(folder_next, folder_list)
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def parse_in_json(in_json, uki_name, distro, arch):
|
|
|
25cfa1 |
with open(in_json, 'r') as f:
|
|
|
25cfa1 |
in_obj = json.load(f)
|
|
|
25cfa1 |
recursively_find_addons(in_obj, [uki_name, distro, arch])
|
|
|
25cfa1 |
|
|
|
25cfa1 |
for addon_name, cmdline in uki_addons.items():
|
|
|
25cfa1 |
addon_name = addon_name.replace(".addon","")
|
|
|
25cfa1 |
addon_full_name = f'{addon_name}-{uki_name}.{distro}.{arch}.addon.efi'
|
|
|
25cfa1 |
cmdline = parse_lines(cmdline).rstrip()
|
|
|
25cfa1 |
if cmdline:
|
|
|
25cfa1 |
uki_addons_list.append(UKICmdlineAddon(addon_full_name, cmdline))
|
|
|
25cfa1 |
|
|
|
25cfa1 |
def create_addons(out_dir):
|
|
|
25cfa1 |
for uki_addon in uki_addons_list:
|
|
|
25cfa1 |
out_path = os.path.join(out_dir, uki_addon.name)
|
|
|
25cfa1 |
cmd = [
|
|
|
25cfa1 |
f'{UKIFY_PATH}', 'build',
|
|
|
25cfa1 |
f'--cmdline="{uki_addon.cmdline}"',
|
|
|
25cfa1 |
f'--output={out_path}']
|
|
|
25cfa1 |
if addon_sbat_string:
|
|
|
25cfa1 |
cmd.append('--sbat="' + addon_sbat_string.rstrip() +'"')
|
|
|
25cfa1 |
|
|
|
25cfa1 |
subprocess.check_call(cmd, text=True)
|
|
|
25cfa1 |
|
|
|
25cfa1 |
if __name__ == "__main__":
|
|
|
25cfa1 |
argc = len(sys.argv) - 1
|
|
|
25cfa1 |
if argc != 5:
|
|
|
25cfa1 |
usage('too few or too many parameters!')
|
|
|
25cfa1 |
|
|
|
25cfa1 |
input_json = sys.argv[1]
|
|
|
25cfa1 |
out_dir = sys.argv[2]
|
|
|
25cfa1 |
uki_name = sys.argv[3]
|
|
|
25cfa1 |
distro = sys.argv[4]
|
|
|
25cfa1 |
arch = sys.argv[5]
|
|
|
25cfa1 |
|
|
|
25cfa1 |
out_dir = check_clean_arguments(input_json, out_dir)
|
|
|
25cfa1 |
parse_in_json(input_json, uki_name, distro, arch)
|
|
|
25cfa1 |
create_addons(out_dir)
|
|
|
25cfa1 |
|
|
|
25cfa1 |
|