userland: try to make userland executable selection saner

Only allow existing files to be built, stop extension expansion madness.

cli_function: get_cli print booleans properly, was printing without --no-
for negations.
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-05 00:00:00 +00:00
parent f1c3b64a55
commit eba97f9cef
11 changed files with 570 additions and 459 deletions

View File

@@ -2,6 +2,8 @@
import os
from shell_helpers import LF
class PathProperties:
'''
Encodes properties of userland and baremetal paths.
@@ -12,30 +14,42 @@ class PathProperties:
self,
**kwargs
):
self.properties = {
'allowed_archs': None,
'exit_status': 0,
'interactive': False,
'more_than_1s': False,
property_keys = {
'allowed_archs',
'c_std',
'cc_flags',
'cc_pedantic',
'cxx_std',
'exit_status',
'interactive',
'skip_run_unclassified',
'more_than_1s',
# The path does not generate an executable in itself, e.g.
# it only generates intermediate object files.
'no_executable': False,
'no_executable',
'pedantic',
# the test receives a signal. We skip those tests for now,
# on userland because we are lazy to figure out the exact semantics
# of how Python + QEMU + gem5 determine the exit status of signals.
'receives_signal': False,
'requires_kernel_modules': False,
'receives_signal',
'requires_kernel_modules',
}
for key in kwargs:
if not key in self.properties:
if not key in property_keys:
raise ValueError('Unknown key: {}'.format(key))
self.properties.update(kwargs)
self.properties = kwargs
def __getitem__(self, key):
return self.properties[key]
def __repr__(self):
return str(self.properties)
def update(self, other):
return self.properties.update(other.properties)
other_tmp_properties = other.properties.copy()
if 'cc_flags' in self.properties and 'cc_flags' in other_tmp_properties:
other_tmp_properties['cc_flags'] = self.properties['cc_flags'] + other_tmp_properties['cc_flags']
return self.properties.update(other_tmp_properties)
def should_be_tested(self, arch):
return \
@@ -44,57 +58,138 @@ class PathProperties:
not self['no_executable'] and \
not self['receives_signal'] and \
not self['requires_kernel_modules'] and \
not self['skip_run_unclassified'] and \
(
self['allowed_archs'] is None or
arch in self['allowed_archs']
)
class PrefixTree:
def __init__(self, children=None, value=None):
if children == None:
def __init__(self, path_properties_dict=None, children=None):
if children is None:
children = {}
if path_properties_dict is None:
path_properties_dict = {}
self.children = children
self.value = value
self.path_properties = PathProperties(**path_properties_dict)
path_properties_tree = PrefixTree({
'arch': PrefixTree({
'x86_64': PrefixTree(
default_c_std = 'c11'
default_cxx_std = 'c++17'
gnu_extensions = {
'c_std': 'gnu11',
'cc_pedantic': False,
'cxx_std': 'gnu++17'
}
path_properties_tree = PrefixTree(
{
'c_std': default_c_std,
'cxx_std': default_cxx_std,
'pedantic': True,
'allowed_archs': None,
'c_std': None,
'cc_flags': [],
'cc_pedantic': True,
'cxx_std': None,
'exit_status': 0,
'interactive': False,
'skip_run_unclassified': False,
'more_than_1s': False,
# The path does not generate an executable in itself, e.g.
# it only generates intermediate object files.
'no_executable': False,
'pedantic': False,
# the test receives a signal. We skip those tests for now,
# on userland because we are lazy to figure out the exact semantics
# of how Python + QEMU + gem5 determine the exit status of signals.
'receives_signal': False,
'requires_kernel_modules': False,
},
{
'userland': PrefixTree(
{
'c': PrefixTree({
'ring0.c': PrefixTree(value=PathProperties(receives_signal=True))
})
},
PathProperties(allowed_archs={'x86_64'}),
),
'arm': PrefixTree(value=PathProperties(allowed_archs={'arm'})),
'aarch64': PrefixTree(value=PathProperties(allowed_archs={'aarch64'})),
'empty.S': PrefixTree(value=PathProperties(no_executable=True)),
'fail.S': PrefixTree(value=PathProperties(no_executable=True)),
'main.c': PrefixTree(value=PathProperties(no_executable=True)),
}),
'c': PrefixTree({
'assert_fail.c': PrefixTree(value=PathProperties(exit_status=1)),
'false.c': PrefixTree(value=PathProperties(exit_status=1)),
'getchar.c': PrefixTree(value=PathProperties(interactive=True)),
'infinite_loop.c': PrefixTree(value=PathProperties(more_than_1s=True)),
}),
'kernel_modules': PrefixTree(value=PathProperties(requires_kernel_modules=True)),
'linux': PrefixTree(value=PathProperties(requires_kernel_modules=True)),
'posix': PrefixTree({
'count.c': PrefixTree(value=PathProperties(more_than_1s=True)),
'sleep_forever.c': PrefixTree(value=PathProperties(more_than_1s=True)),
'virt_to_phys_test.c': PrefixTree(value=PathProperties(more_than_1s=True)),
})
})
{
'arch': PrefixTree(
{
'cc_flags': [
'-fno-pie', LF,
'-no-pie', LF,
]
},
{
'arm': PrefixTree(
{
'allowed_archs': {'arm'},
'cc_flags': [
'-Xassembler', '-mcpu=cortex-a72', LF,
# To prevent:
# > vfp.S: Error: selected processor does not support <FPU instruction> in ARM mode
# https://stackoverflow.com/questions/41131432/cross-compiling-error-selected-processor-does-not-support-fmrx-r3-fpexc-in/52875732#52875732
# We aim to take the most extended mode currently available that works on QEMU.
'-Xassembler', '-mfpu=crypto-neon-fp-armv8.1', LF,
'-Xassembler', '-meabi=5', LF,
# Treat inline assembly as arm instead of thumb
# The opposite of -mthumb.
'-marm', LF,
# Make gcc generate .syntax unified for inline assembly.
# However, it gets ignored if -marm is given, which a GCC bug that was recently fixed:
# https://stackoverflow.com/questions/54078112/how-to-write-syntax-unified-ual-armv7-inline-assembly-in-gcc/54132097#54132097
# So we just write divided inline assembly for now.
'-masm-syntax-unified', LF,
]
}
),
'aarch64': PrefixTree({'allowed_archs': {'aarch64'}}),
'empty.S': PrefixTree({'no_executable': True}),
'fail.S': PrefixTree({'no_executable': True}),
'main.c': PrefixTree({'no_executable': True}),
'x86_64': PrefixTree(
{'allowed_archs': {'x86_64'}},
{
'c': PrefixTree(
{},
{
'ring0.c': PrefixTree({'receives_signal': True})
}
),
}
),
}
),
'c': PrefixTree(
{},
{
'assert_fail.c': PrefixTree({'exit_status': 1}),
'false.c': PrefixTree({'exit_status': 1}),
'getchar.c': PrefixTree({'interactive': True}),
'infinite_loop.c': PrefixTree({'more_than_1s': True}),
}
),
'gcc': PrefixTree(gnu_extensions),
'kernel_modules': PrefixTree({**gnu_extensions, **{'requires_kernel_modules': True}}),
'linux': PrefixTree(
{**gnu_extensions, **{'skip_run_unclassified': True}},
),
'posix': PrefixTree(
{},
{
'count.c': PrefixTree({'more_than_1s': True}),
'sleep_forever.c': PrefixTree({'more_than_1s': True}),
'virt_to_phys_test.c': PrefixTree({'more_than_1s': True}),
}
)
}
)
}
)
def get(test_path):
cur_node = path_properties_tree
path_properties = PathProperties()
path_properties = PathProperties(**cur_node.path_properties.properties)
for path_component in test_path.split(os.sep):
if path_component in cur_node.children:
cur_node = cur_node.children[path_component]
if cur_node.value is not None:
path_properties.update(cur_node.value)
path_properties.update(cur_node.path_properties)
else:
break
return path_properties