tools: kexttool: implement Makefile generation for internal kexts
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
questions = [
|
out_file = open(selection_file_path, 'w')
|
||||||
inquirer.Checkbox('kexts',
|
selection_info = {}
|
||||||
message="Select kexts to include in the kernel image",
|
|
||||||
choices=all_kexts,
|
for k in all_kexts:
|
||||||
)
|
k_info = {
|
||||||
]
|
'description': description_to_list(k),
|
||||||
answers = inquirer.prompt(questions)
|
'build': None
|
||||||
print(answers["kexts"])
|
}
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
def create_kext_makefile():
|
||||||
|
all_kexts = KextSource.scan(os.path.join(os.getcwd(), 'kexts'))
|
||||||
|
selection_info = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
selection_fp = open(selection_file_path, 'r')
|
||||||
|
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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user