kiwi-el8/helper/completion_generator
Neal Gompa 233e6e2835 Use the versioned Python interpreter path to run build helper scripts
The kiwi build process mostly does the right thing in terms of executing
with the correct Python interpreter throughout the build process, with
the exception of the Makefile not correctly locating the versioned Python
interpreter executable path, and the bash completion generator being executed
using '/usr/bin/python' without regard for what the target environment was.

This is a problem when a build environment complying with PEP 394 as it
stands today does not have Python 2 installed, such as when kiwi is being
built as a Python 3-only package in Fedora. Thus, the Makefile has been
adjusted to not only correctly locate the versioned Python interpreter, but
to also execute 'completion_helper' with the correct interpreter.

Additionally, a trivial change to the shebang to 'completion_helper'
was made to be consistent with other Python-based build helper scripts.

This is part of the overall effort to eliminate the dependencies on
Python 2 in Fedora.

Reference: https://fedoraproject.org/wiki/FinalizingFedoraSwitchtoPython3
2018-06-06 08:45:52 -04:00

253 lines
9.0 KiB
Python
Executable File

#!/usr/bin/env python
from textwrap import dedent
import subprocess
import re
import sys
import collections
import pprint
class AutoVivification(dict):
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
class AppHash:
def __init__(self):
tasks = [
'kiwi/cli.py',
'kiwi/tasks/*.py'
]
call_parm = ['bash', '-c', 'cat %s' % ' '.join(tasks)]
cmd = subprocess.Popen(
call_parm, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
)
begin = False
cur_path = ''
self.result = AutoVivification()
for line in cmd.communicate()[0].decode().split('\n'):
usage = re.search('^usage: (.*)', line)
if usage:
begin = True
if re.match('.*--help', line):
mod_line = re.sub('[\[\]\|]', '', usage.group(1))
mod_line = re.sub('-h ', '', mod_line)
key_list = mod_line.split()
result_keys = self.validate(key_list)
cur_path = self.merge(result_keys, self.result)
else:
key_list = usage.group(1).split()
result_keys = self.validate(key_list)
cur_path = self.merge(result_keys, self.result)
elif begin:
if not line:
begin = False
else:
if re.match('.*--version', line):
mod_line = re.sub('[\[\]\|]', '', line)
mod_line = re.sub('-v ', '', mod_line)
key_list = mod_line.split()
result_keys = self.validate(key_list)
cur_path = self.merge(result_keys, self.result)
elif re.match('.*kiwi --compat', line):
mod_line = re.sub('[\[\]\|]', '', line)
mod_line = re.sub('<legacy_args>...', '', mod_line)
key_list = mod_line.split()
result_keys = self.validate(key_list)
cur_path = self.merge(result_keys, self.result)
elif re.match('.*kiwi \[', line):
line = line.replace('[', '')
line = line.replace(']', '')
line = line.replace('|', '')
key_list = line.split()
key_list.pop(0)
for global_opt in key_list:
result_keys = self.validate(
['kiwi', global_opt]
)
cur_path = self.merge(result_keys, self.result)
elif re.match(' \[', line):
line = line.replace('[', '')
line = line.replace(']', '')
line = line.replace('|', '')
key_list = line.split()
for global_opt in key_list:
result_keys = self.validate(
['kiwi', global_opt]
)
cur_path = self.merge(result_keys, self.result)
elif re.match(' \[', line):
opt_val = re.search(' \[(--.*)', line)
global_opt = opt_val.group(1)
global_opt = global_opt.replace('[', '')
global_opt = global_opt.replace(']', '')
global_opt = global_opt.replace('|', '')
result_keys = self.validate(
['kiwi', global_opt]
)
cur_path = self.merge(result_keys, self.result)
elif re.match('.*kiwi', line):
mandatory_options = re.search(
'(.*kiwi.*?) (--.*)', line
)
if mandatory_options:
line = mandatory_options.group(1)
key_list = line.split()
result_keys = self.validate(key_list)
cur_path = self.merge(result_keys, self.result)
if mandatory_options:
for option in mandatory_options.group(2).split():
mod_line = cur_path + ' ' + option
key_list = mod_line.split()
result_keys = self.validate(key_list)
self.merge(result_keys, self.result)
else:
if 'kiwi --' in cur_path:
cur_path = ''
for mod_line in line.strip().split('|'):
mod_line = cur_path + ' ' + mod_line
mod_line = re.sub('[\[\]]', '', mod_line)
mod_line = re.sub('-h ', '', mod_line)
key_list = mod_line.split()
result_keys = self.validate(key_list)
self.merge(result_keys, self.result)
def merge(self, key_list, result):
raw_key_path = " ".join(key_list)
key_path = ''
for key in key_list:
key_path += '[\'' + key + '\']'
expression = 'self.result' + key_path
exec(expression)
return raw_key_path
def validate(self, key_list):
result_keys = []
for key in key_list:
option = re.search('^\[(--.*)=|^\[(.*)\]', key)
mandatory = re.search('^(--.*)=', key)
if option:
if option.group(1):
result_keys.append(option.group(1))
elif mandatory:
if mandatory.group(1):
result_keys.append(mandatory.group(1))
elif re.search('<servicename>|<command>|<args>...', key):
pass
else:
key = key.replace('<','__')
key = key.replace('>','__')
result_keys.append(key)
return result_keys
class AppTree:
def __init__(self):
self.completion = AppHash()
self.level_dict = {}
def traverse(self, tree=None, level=0, origin=None):
if not tree:
tree = self.completion.result['kiwi']
if not origin:
origin = 'kiwi'
for key in tree:
try:
if self.level_dict[str(level)]:
pass
except KeyError:
self.level_dict[str(level)] = {}
try:
if self.level_dict[str(level)][origin]:
pass
except KeyError:
self.level_dict[str(level)][origin] = []
if not key in self.level_dict[str(level)][origin]:
self.level_dict[str(level)][origin].append(key)
if tree[key]:
self.traverse(tree[key], level + 1, key)
tree = AppTree()
tree.traverse()
# helpful for debugging
# pp = pprint.PrettyPrinter(indent=4)
# pp.pprint(tree.completion.result)
sorted_levels = collections.OrderedDict(
sorted(tree.level_dict.items())
)
print(dedent('''
#========================================
# _kiwi
#----------------------------------------
function setupCompletionLine {
local comp_line=$(echo $COMP_LINE | sed -e 's@kiwi-ng@kiwi@')
local result_comp_line
local prev_was_option=0
for item in $comp_line; do
if [ $prev_was_option = 1 ];then
prev_was_option=0
continue
fi
if [[ $item =~ -.* ]];then
prev_was_option=1
continue
fi
result_comp_line="$result_comp_line $item"
done
echo $result_comp_line
}
function _kiwi {
local cur prev opts
_get_comp_words_by_ref cur prev
local cmd=$(setupCompletionLine | awk -F ' ' '{ print $NF }')
''').strip())
print(' for comp in $prev $cmd;do')
print(' case "$comp" in')
for level in sorted_levels:
if level == '0':
continue
for sub in sorted(sorted_levels[level]):
print(' "%s")' % (sub))
print(' __comp_reply "%s"' % \
(" ".join(sorted(sorted_levels[level][sub])))
)
print(' return 0')
print(' ;;')
print(' esac')
print(' done')
print(' __comp_reply "%s"' % \
(" ".join(sorted(sorted_levels['0']['kiwi'])))
)
print(' return 0')
print('}')
print(dedent('''
#========================================
# comp_reply
#----------------------------------------
function __comp_reply {
word_list=$@
COMPREPLY=($(compgen -W "$word_list" -- ${cur}))
}
complete -F _kiwi -o default kiwi
complete -F _kiwi -o default kiwi-ng
complete -F _kiwi -o default kiwi-ng-2
complete -F _kiwi -o default kiwi-ng-3
''').strip())