Skip to content

Commit

Permalink
Improve multi_targets() method:
Browse files Browse the repository at this point in the history
 - Split the function into multiple smaller functions for better organization.
 - Modify the function to return a single library array instead of a list of libraries.
 - Avoid returning the result of testing the enabled features to keep it
   simple.
 - Enhance debugging capabilities by printing the test results of enabled targets.
  • Loading branch information
seiko2plus committed Aug 4, 2023
1 parent 3e634e5 commit e013e8a
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 66 deletions.
176 changes: 111 additions & 65 deletions mesonbuild/modules/features/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,7 @@ def fail_result(fail_reason: str, is_disabled: bool = False,
)
def multi_targets_method(self, state: 'ModuleState',
args: T.Tuple[str], kwargs: 'TYPE_kwargs'
) -> T.List[T.Union[
T.Dict[str, 'TestResultKwArgs'],
T.List[build.StaticLibrary]
]]:
) -> build.StaticLibrary:
config_name = args[0]
sources = args[1] # type: ignore
dispatch: T.List[T.Union[FeatureObject, T.List[FeatureObject]]] = (
Expand All @@ -405,7 +402,6 @@ def multi_targets_method(self, state: 'ModuleState',
if not compiler:
compiler = get_compiler(state)

enabled_targets: T.Dict[str, 'TestResultKwArgs'] = {}
baseline_features : T.Set[FeatureObject] = set()
has_baseline = baseline is not None
if has_baseline:
Expand All @@ -416,15 +412,16 @@ def multi_targets_method(self, state: 'ModuleState',
compiler=compiler,
force_args=None
)
enabled_targets['baseline'] = baseline_test_result
baseline_test_result['target_name'] = 'baseline'

enabled_targets_names: T.List[str] = []
enabled_targets_features: T.List[T.Union[
FeatureObject, T.List[FeatureObject]
]] = []
enabled_targets_tests: T.List['TestResultKwArgs'] = []
skipped_targets: T.List[T.Tuple[
T.Union[FeatureObject, T.List[FeatureObject]], str
]] = []
enabled_dispatch: T.List[
T.Union[FeatureObject, T.List[FeatureObject]]
] = []
dispatch_calls: T.List[str] = []
for d in dispatch:
if isinstance(d, FeatureObject):
target = {d,}
Expand All @@ -447,74 +444,63 @@ def multi_targets_method(self, state: 'ModuleState',
)
continue
target_name = test_result['target_name']
if target_name in enabled_targets:
if target_name in enabled_targets_names:
skipped_targets.append((
d, f'Dublicate target name "{target_name}"'
))
continue
enabled_targets[target_name] = test_result
enabled_dispatch.append(d)
c_detect = '&&'.join([
f'TEST_CB({d})' for d in test_result['detect']
])
if c_detect:
c_detect = f'({c_detect})'
else:
c_detect = '1'
dispatch_calls.append(
f'{prefix}_MTARGETS_EXPAND('
f'EXEC_CB({c_detect}, {target_name}, __VA_ARGS__)'
')'
)
enabled_targets_names.append(target_name)
enabled_targets_features.append(d)
enabled_targets_tests.append(test_result)

if not kwargs.pop('keep_sort'):
dispatch_sorted = self.sort_multi(enabled_dispatch, reverse=True)
if enabled_dispatch != dispatch_sorted:
enabled_targets_sorted = self.sort_multi(enabled_targets_features, reverse=True)
if enabled_targets_features != enabled_targets_sorted:
raise MesonException(
'The enabled dispatch features should be sorted based on the highest interest:\n'
f'Expected: {self.features_names(dispatch_sorted)}\n'
f'Got: {self.features_names(enabled_dispatch)}\n'
f'Expected: {self.features_names(enabled_targets_sorted)}\n'
f'Got: {self.features_names(enabled_targets_features)}\n'
'Note: This validation may not be accurate when dealing with multi-features per single target.\n'
'You can keep the current sort and bypass this validation by passing the argument "keep_sort: true".'
)

config_file = [
'/* Autogenerated by the Meson features module. */',
'/* Do not edit, your changes will be lost. */',
'',
f'#undef {prefix}_MTARGETS_EXPAND',
f'#define {prefix}_MTARGETS_EXPAND(X) X',
'',
f'#undef {prefix}MTARGETS_CONF_BASELINE',
f'#define {prefix}MTARGETS_CONF_BASELINE(EXEC_CB, ...) ' + (
f'{prefix}_MTARGETS_EXPAND(EXEC_CB(__VA_ARGS__))'
if has_baseline else ''
),
'',
f'#undef {prefix}MTARGETS_CONF_DISPATCH',
f'#define {prefix}MTARGETS_CONF_DISPATCH(TEST_CB, EXEC_CB, ...) \\',
' \\\n'.join(dispatch_calls),
'',
]

build_dir = state.environment.build_dir
sub_dir = state.subdir
if sub_dir:
build_dir = os.path.join(build_dir, sub_dir)
config_path = os.path.abspath(os.path.join(build_dir, config_name))
config_path = self.gen_config(
state,
config_name=config_name,
targets=enabled_targets_tests,
prefix=prefix,
has_baseline=has_baseline
)

os.makedirs(os.path.dirname(config_path), exist_ok=True)
with open(config_path, "w", encoding='utf-8') as cout:
cout.write('\n'.join(config_file))
if has_baseline:
enabled_targets_tests += [baseline_test_result]
enabled_targets_names += ['baseline']

static_libs = []
for target_name, test_result in enabled_targets.items():
static_libs.append(self.gen_target(
link_with = [
self.gen_target(
state=state, config_name=config_name,
sources=sources, test_result=test_result,
prefix=prefix, is_baseline=target_name == 'baseline',
prefix=prefix, is_baseline=test_result['target_name'] == 'baseline',
stlib_kwargs=kwargs
))
)
# The linking order is based on the lowest interested features.
# This ensures that the linker prioritizes any duplicate symbols
# of the lowest interested features over the highest ones,
# starting with the baseline to avoid any possible crashes due
# to any involved optimizations that may generated based
# on the highest interested features.
for test_result in reversed(enabled_targets_tests)
]
# gather the generated libs under single object
static_lib: build.StaticLibrary = state._interpreter.func_static_lib(
None, [config_name],
{
'link_whole': link_with,
# the config path inplaced, since Meson deprecate
# having empty sources.
'sources': config_path
}
)

skipped_targets_info: T.List[str] = []
skipped_tab = ' '*4
Expand All @@ -525,20 +511,29 @@ def multi_targets_method(self, state: 'ModuleState',
)
skipped_targets_info.append(f'{skipped_tab}"{name}": "{reason}"')

enabled_targets_info: T.List[str] = [
f'{skipped_tab}"{test_result["target_name"]}":\n' + '\n'.join([
f'{skipped_tab*2}"{k}": {v}'
for k, v in test_result.items()
])
for test_result in enabled_targets_tests
]

mlog.log(
f'Generating multi-targets for "{mlog.bold(config_name)}"',
'\n Enabled targets:',
mlog.green(', '.join(enabled_targets.keys()))
mlog.green(', '.join(enabled_targets_names))
)
mlog.debug(
f'Generating multi-targets for "{config_name}"',
'\n Config path:', config_path,
'\n Enabled targets:', ', '.join(enabled_targets.keys()),
'\n Skipped:',
'\n Enabled targets:',
'\n'+'\n'.join(enabled_targets_info),
'\n Skipped targets:',
'\n'+'\n'.join(skipped_targets_info),
'\n'
)
return [enabled_targets, static_libs]
return static_lib

def gen_target(self, state: 'ModuleState', config_name: str,
sources: T.List[T.Union[
Expand Down Expand Up @@ -570,6 +565,57 @@ def gen_target(self, state: 'ModuleState', config_name: str,
)
return static_lib

def gen_config(self, state: 'ModuleState', config_name: str,
targets: T.List['TestResultKwArgs'],
prefix: str, has_baseline: bool
) -> str:

dispatch_calls: T.List[str] = []
for test in targets:
c_detect = '&&'.join([
f'TEST_CB({d})' for d in test['detect']
])
if c_detect:
c_detect = f'({c_detect})'
else:
c_detect = '1'
dispatch_calls.append(
f'{prefix}_MTARGETS_EXPAND('
f'EXEC_CB({c_detect}, {test["target_name"]}, __VA_ARGS__)'
')'
)

config_file = [
'/* Autogenerated by the Meson features module. */',
'/* Do not edit, your changes will be lost. */',
'',
f'#undef {prefix}_MTARGETS_EXPAND',
f'#define {prefix}_MTARGETS_EXPAND(X) X',
'',
f'#undef {prefix}MTARGETS_CONF_BASELINE',
f'#define {prefix}MTARGETS_CONF_BASELINE(EXEC_CB, ...) ' + (
f'{prefix}_MTARGETS_EXPAND(EXEC_CB(__VA_ARGS__))'
if has_baseline else ''
),
'',
f'#undef {prefix}MTARGETS_CONF_DISPATCH',
f'#define {prefix}MTARGETS_CONF_DISPATCH(TEST_CB, EXEC_CB, ...) \\',
' \\\n'.join(dispatch_calls),
'',
]

build_dir = state.environment.build_dir
sub_dir = state.subdir
if sub_dir:
build_dir = os.path.join(build_dir, sub_dir)
config_path = os.path.abspath(os.path.join(build_dir, config_name))

os.makedirs(os.path.dirname(config_path), exist_ok=True)
with open(config_path, "w", encoding='utf-8') as cout:
cout.write('\n'.join(config_file))

return config_path

@typed_pos_args('features.sort', varargs=FeatureObject, min_varargs=1)
@typed_kwargs('features.sort',
KwargInfo('reverse', bool, default = False),
Expand Down
2 changes: 1 addition & 1 deletion test cases/features/2 multi_targets/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ dispatch3 = mod_features.multi_targets(

exe = executable(
'multi_targets', 'main.c',
link_with: dispatch1[1] + dispatch2[1] + dispatch3[1]
link_with: dispatch1 + dispatch2 + dispatch3
)

test('multi_targets', exe)

0 comments on commit e013e8a

Please sign in to comment.