Skip to content

Commit

Permalink
android: Added android_usecase kwargs to executable
Browse files Browse the repository at this point in the history
By setting android_usecase to `application`, the executable gets
actually built as a shared library instead of an executable. This makes
it possible to use an application within an android application process.

#13758
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/7555/
  • Loading branch information
sp1ritCS committed Oct 9, 2024
1 parent 7cb3674 commit 306eeec
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 11 deletions.
11 changes: 11 additions & 0 deletions docs/yaml/functions/executable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ varargs_inherit: _build_target_base
kwargs_inherit: _build_target_base

kwargs:
android_usecase:
type: str
default: "'executable'"
since: 1.6.0
description: |
Specifies the intended target of the executable. This can either be
`executable`, if the intended usecase is to run the executable using
fork + exec, or `application` if the executable is supposed to be
loaded as shared object by the android runtime.
export_dynamic:
type: bool
since: 0.45.0
Expand Down
6 changes: 5 additions & 1 deletion mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,11 @@ def generate_basic_compiler_args(self, target: build.BuildTarget, compiler: 'Com
if isinstance(target, build.StaticLibrary) and target.pic:
commands += compiler.get_pic_args()
elif isinstance(target, (build.StaticLibrary, build.Executable)) and target.pie:
commands += compiler.get_pie_args()
m = self.environment.machines[target.for_machine]
if isinstance(target, build.Executable) and m.is_android() and target.android_usecase == 'application':
commands += compiler.get_pic_args()
else:
commands += compiler.get_pie_args()
# Add compile args needed to find external dependencies. Link args are
# added while generating the link command.
# NOTE: We must preserve the order in which external deps are
Expand Down
9 changes: 7 additions & 2 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3261,8 +3261,13 @@ def get_target_type_link_args(self, target, linker):
# If implib, and that's significant on this platform (i.e. Windows using either GCC or Visual Studio)
if target.import_filename:
commands += linker.gen_import_library_args(self.get_import_filename(target))
if target.pie:
commands += linker.get_pie_link_args()
m = self.environment.machines[target.for_machine]
if m.is_android() and target.android_usecase == 'application':
commands += linker.get_pic_args()
commands += linker.get_std_shared_module_link_args(target.get_options())
else:
if target.pie:
commands += linker.get_pie_link_args()
if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'):
commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src))
elif isinstance(target, build.SharedLibrary):
Expand Down
18 changes: 14 additions & 4 deletions mesonbuild/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class DFeatures(TypedDict):
rust_kwargs |
cs_kwargs)

known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs'}
known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs', 'android_usecase'}
known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi'}
known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs', 'rust_abi'}
known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink', 'rust_abi'}
Expand Down Expand Up @@ -1966,6 +1966,9 @@ def __init__(
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
self.win_subsystem = kwargs.get('win_subsystem') or 'console'
self.android_usecase = kwargs.get('android_usecase', 'application' if kwargs.get('gui_app') else 'executable')
if (not isinstance(self.android_usecase, str)) or self.android_usecase not in ['executable', 'application']:
raise InvalidArguments('"android_usecase" keyword must be one of: executable, application')
# Check for export_dynamic
self.export_dynamic = kwargs.get('export_dynamic', False)
if not isinstance(self.export_dynamic, bool):
Expand All @@ -1984,14 +1987,19 @@ def __init__(
def post_init(self) -> None:
super().post_init()
machine = self.environment.machines[self.for_machine]
# Unless overridden, executables have no suffix or prefix. Except on
# Windows and with C#/Mono executables where the suffix is 'exe'
# Unless overridden, Windows and C#/Mono executables have 'exe' as suffix and Android 'application'
# executables follow the shared library naming conventions (as they are shared libraries).
if not hasattr(self, 'prefix'):
self.prefix = ''
if machine.is_android() and self.android_usecase == 'application':
self.prefix = 'lib'
else:
self.prefix = ''
if not hasattr(self, 'suffix'):
# Executable for Windows or C#/Mono
if machine.is_windows() or machine.is_cygwin() or 'cs' in self.compilers:
self.suffix = 'exe'
elif machine.is_android() and self.android_usecase == 'application':
self.suffix = 'so'
elif machine.system.startswith('wasm') or machine.system == 'emscripten':
self.suffix = 'js'
elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('armclang') or
Expand All @@ -2011,6 +2019,8 @@ def post_init(self) -> None:
else:
self.suffix = machine.get_exe_suffix()
self.filename = self.name
if self.prefix:
self.filename = self.prefix + self.filename
if self.suffix:
self.filename += '.' + self.suffix
self.outputs[0] = self.filename
Expand Down
7 changes: 5 additions & 2 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3448,14 +3448,17 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs
if targetclass is build.Executable:
kwargs = T.cast('kwtypes.Executable', kwargs)
if kwargs['gui_app'] is not None:
if kwargs['win_subsystem'] is not None:
if kwargs['win_subsystem'] is not None or kwargs['android_usecase']:
raise InvalidArguments.from_node(
'Executable got both "gui_app", and "win_subsystem" arguments, which are mutually exclusive',
'Executable got both "gui_app", and "win_subsystem"/"android_usecase" arguments, which are mutually exclusive',
node=node)
if kwargs['gui_app']:
kwargs['win_subsystem'] = 'windows'
kwargs['android_usecase'] = 'application'
if kwargs['win_subsystem'] is None:
kwargs['win_subsystem'] = 'console'
if kwargs['android_usecase'] is None:
kwargs['android_usecase'] = 'executable'

if kwargs['implib']:
if kwargs['export_dynamic'] is False:
Expand Down
1 change: 1 addition & 0 deletions mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ class Executable(_BuildTarget):
pie: T.Optional[bool]
vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]]
win_subsystem: T.Optional[str]
android_usecase: T.Optional[str]


class _StaticLibMixin(TypedDict):
Expand Down
12 changes: 12 additions & 0 deletions mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,13 @@ def _validate_win_subsystem(value: T.Optional[str]) -> T.Optional[str]:
return None


def _validate_android_usecase(value: T.Optional[str]) -> T.Optional[str]:
if value is not None:
if value not in ['application', 'executable']:
return f'Invalid value for win_subsystem: {value}.'
return None


def _validate_darwin_versions(darwin_versions: T.List[T.Union[str, int]]) -> T.Optional[str]:
if len(darwin_versions) > 2:
return f"Must contain between 0 and 2 elements, not {len(darwin_versions)}"
Expand Down Expand Up @@ -710,6 +717,11 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup
convertor=lambda x: x.lower() if isinstance(x, str) else None,
validator=_validate_win_subsystem,
),
KwargInfo(
'android_usecase',
(str, NoneType),
validator=_validate_android_usecase,
),
]

# The total list of arguments used by Executable
Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/mintro.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ def list_targets(builddata: build.Build, installdata: backends.InstallData, back
win_subsystem = getattr(target, 'win_subsystem', None)
if win_subsystem is not None:
t['win_subsystem'] = win_subsystem
android_usecase = getattr(target, 'android_usecase', None)
if android_usecase is not None:
t['android_usecase'] = android_usecase

if installdata and target.should_install():
t['installed'] = True
Expand Down
3 changes: 2 additions & 1 deletion run_project_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class ArgumentType(CompilerArgumentType):
v: bool

ALL_TESTS = ['cmake', 'common', 'native', 'warning-meson', 'failing-meson', 'failing-build', 'failing-test',
'keyval', 'platform-osx', 'platform-windows', 'platform-linux',
'keyval', 'platform-osx', 'platform-windows', 'platform-linux', 'platform-android',
'java', 'C#', 'vala', 'cython', 'rust', 'd', 'objective c', 'objective c++',
'fortran', 'swift', 'cuda', 'python3', 'python', 'fpga', 'frameworks', 'nasm', 'wasm', 'wayland',
'format',
Expand Down Expand Up @@ -1111,6 +1111,7 @@ def __init__(self, category: str, subdir: str, skip: bool = False, stdout_mandat
TestCategory('platform-osx', 'osx', not mesonlib.is_osx()),
TestCategory('platform-windows', 'windows', not mesonlib.is_windows() and not mesonlib.is_cygwin()),
TestCategory('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()),
TestCategory('platform-android', 'android', not mesonlib.is_android()),
TestCategory('java', 'java', backend is not Backend.ninja or not have_java()),
TestCategory('C#', 'csharp', skip_csharp(backend)),
TestCategory('vala', 'vala', backend is not Backend.ninja or not shutil.which(os.environ.get('VALAC', 'valac'))),
Expand Down
15 changes: 15 additions & 0 deletions test cases/android/1 usecase/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
project('android usecase', 'c')
fs = import('fs')

e = executable('executable', 'usecase.c',
android_usecase : 'executable')
a = executable('application', 'usecase.c',
android_usecase : 'application')

if fs.name(e.full_path()).contains('.')
error('Executable with usecase `executable` did have expected filename')
endif

if not fs.name(a.full_path()).startswith('lib') or not fs.name(a.full_path()).endswith('.so')
error('Executable with usecase `application` did not have expected filename')
endif
5 changes: 5 additions & 0 deletions test cases/android/1 usecase/usecase.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <stdio.h>

int main(void) {
return 0;
}
3 changes: 2 additions & 1 deletion unittests/allplatformstests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3327,6 +3327,7 @@ def assertKeyTypes(key_type_list, obj, strict: bool = True):
('depends', list),
('install_filename', (list, None)),
('installed', bool),
('android_usecase', (str, None)),
('vs_module_defs', (str, None)),
('win_subsystem', (str, None)),
]
Expand Down Expand Up @@ -3540,7 +3541,7 @@ def test_introspect_targets_from_source(self):
res_wb = [i for i in res_wb if i['type'] != 'custom']
for i in res_wb:
i['filename'] = [os.path.relpath(x, self.builddir) for x in i['filename']]
for k in ('install_filename', 'dependencies', 'win_subsystem'):
for k in ('install_filename', 'dependencies', 'android_usecase', 'win_subsystem'):
if k in i:
del i[k]

Expand Down

0 comments on commit 306eeec

Please sign in to comment.