tools: kexttool: implement Makefile generation for internal kexts

This commit is contained in:
2023-04-06 21:20:20 +01:00
parent 3e2164c7c2
commit e472c83d28
4 changed files with 206 additions and 18 deletions

View File

@@ -4,9 +4,11 @@ import os
import glob import glob
class KextSource: class KextSource:
__kext_info = {} src_languages = {
__src_dir_path = '' 'C': [ '.c' ],
'CXX': [ '.cpp' ],
'ASM': [ '.S' ],
}
def scan(path): def scan(path):
result = [] result = []
@@ -29,11 +31,34 @@ class KextSource:
self.__kext_info = yaml.safe_load(info_fp) self.__kext_info = yaml.safe_load(info_fp)
def __extension_is_of_lang(ext, lang_name):
for name, exts in KextSource.src_languages.items():
if name == lang_name and ext in exts:
return True
return False
def __extension_get_lang(ext):
for name, exts in src_languages.items():
if ext in exts:
return name
return None
def name(self): def name(self):
return self.__kext_info['name'] return self.__kext_info['name']
def id(self): def description(self):
return self.__kext_info['description']
def id(self, underscores=False):
if underscores:
return self.__kext_info['id'].replace('.', '_').replace('-', '_')
return self.__kext_info['id'] return self.__kext_info['id']
@@ -49,5 +74,33 @@ class KextSource:
return self.__kext_info['sources'] return self.__kext_info['sources']
def source_languages(self):
langs = []
for src in self.__kext_info['sources']:
ext = os.path.splitext(src)[1]
lang = KextSource.__extension_get_lang(ext)
if lang == None:
continue
langs.append(lang)
return langs
def sources_filepath(self, lang=None):
sources = []
for s in self.__kext_info['sources']:
if lang != None:
ext = os.path.splitext(s)[1]
if KextSource.__extension_is_of_lang(lang, ext):
continue
full_path = os.path.join(self.__src_dir_path, s)
sources.append(full_path)
return sources
def src_dirpath(self): def src_dirpath(self):
return self.__src_dir_path return self.__src_dir_path

View File

@@ -4,7 +4,7 @@ from kexttool import kext
def list_all(pretty=False): def list_all(pretty=False):
cwd = os.getcwd() cwd = os.getcwd()
scan_arg = os.path.join(cwd, 'extensions', '**/extension.yaml') scan_arg = os.path.join(cwd, 'kexts', '**/extension.yaml')
nr_extensions = 0 nr_extensions = 0

View File

@@ -1,19 +1,155 @@
import inquirer import yaml
import os import os
from kexttool import kext from kexttool.kext import KextSource
def select_kexts(): selection_file_path = 'build/extensions.yaml'
all_kexts = [ k.id() for k in kext.KextSource.scan(os.path.join(os.getcwd(), 'extensions')) ] kext_makefile_path = 'build/extensions.mk'
def description_to_list(kext):
return list(filter(None, kext.description().split('\n')))
def create_selection_file():
if not os.path.isdir('build'):
os.mkdir('build')
all_kexts = KextSource.scan(os.path.join(os.getcwd(), 'kexts'))
if len(all_kexts) == 0: if len(all_kexts) == 0:
print('No kernel extensions available.') print('No kernel extensions available.')
return return
out_file = open(selection_file_path, 'w')
selection_info = {}
for k in all_kexts:
k_info = {
'description': description_to_list(k),
'build': None
}
selection_info[k.id()] = k_info
out_file.write(yaml.dump(selection_info, default_flow_style=False))
print('A list of available kexts has been created at {}'.format(selection_file_path))
print('For each kext that you want to build, set the "build" key to:')
print(' * internal: build the kext into the kernel image, to be loaded during boot.')
print(' * external: build the kext into a standalone executable image, to be loaded at runtime.')
print(' * null (or any other value): don\'t build the kext.')
return
def find_kext_by_id(all_kexts, ident):
for k in all_kexts:
if k.id() == ident:
return k
return None
def external_kext_src_variable_name(kext, lang):
return 'KEXT_{}_{}_SRC'.format(kext.id(underscores=True), lang)
def external_kext_obj_variable_name(kext):
return 'KEXT_{}_OBJ'.format(kext.id(underscores=True))
def write_external_kext_source_list(fp, kext, lang):
sources = kext.sources_filepath(lang)
fp.write('{} := \\\n'.format(external_kext_src_variable_name(kext, lang)))
for i, src in enumerate(sources):
fp.write('\t{}'.format(src))
if i != len(sources) - 1:
fp.write(' \\')
fp.write('\n')
def write_external_kext_variables(fp, kexts):
for kext in kexts:
fp.write('{} := \\\n'.format(external_kext_obj_variable_name(kext)))
sources = kext.sources_filepath()
for i, src in enumerate(sources):
obj = os.path.splitext(src)[0] + '.o'
fp.write('\t$(BUILD_DIR)/{}'.format(obj))
if i != len(sources) - 1:
fp.write(' \\')
fp.write('\n')
questions = [
inquirer.Checkbox('kexts', def create_kext_makefile():
message="Select kexts to include in the kernel image", all_kexts = KextSource.scan(os.path.join(os.getcwd(), 'kexts'))
choices=all_kexts, selection_info = {}
)
] try:
answers = inquirer.prompt(questions) selection_fp = open(selection_file_path, 'r')
print(answers["kexts"]) selection_info = yaml.safe_load(selection_fp)
except Exception as e:
print('E: cannot load extension selection data ({})'.format(e))
return
try:
makefile_fp = open(kext_makefile_path, 'w')
except Exception as e:
print('E: cannot write extension makefile ({})'.format(e))
return
internal_source_files = []
external_kexts = []
print('the following kernel extensions will be built:')
for kext_id, build_info in selection_info.items():
try:
kext_info = find_kext_by_id(all_kexts, kext_id)
if kext_info == None:
print('W: cannot find kernel extension with ID "{}"'.format(kext_id))
continue
if 'build' not in build_info:
continue
if build_info['build'] != 'internal' and build_info['build'] != 'external':
continue
if build_info['build'] == 'internal':
internal_source_files += kext_info.sources_filepath()
elif build_info['build'] == 'external':
external_kexts.append(kext_info)
else:
continue
print(' * {} ({})'.format(kext_info.id(), build_info['build']))
except Exception as e:
print(e)
continue
if len(internal_source_files) > 0:
makefile_fp.write('INTERNAL_KEXT_OBJ := \\\n')
for i, src in enumerate(internal_source_files):
obj = os.path.splitext(src)[0] + '.o'
makefile_fp.write('\t$(BUILD_DIR)/{}'.format(obj))
if i != len(internal_source_files) - 1:
makefile_fp.write(' \\')
makefile_fp.write('\n')
write_external_kext_variables(makefile_fp, external_kexts)
for kext in external_kexts:
makefile_fp.write('\nkexts/{}: $({})\n'.format(kext.id(), external_kext_obj_variable_name(kext)))
def select_kexts():
if os.path.isfile(selection_file_path):
create_kext_makefile()
else:
create_selection_file()

View File

@@ -3,7 +3,6 @@
# -*- mode: python -*- # -*- mode: python -*-
import glob import glob
import inquirer
import os import os
import sys import sys
from kexttool import kext, scan, select from kexttool import kext, scan, select