From 5a5fb9fe55f4945246ce4c6f8bd924bc2033fc65 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Sat, 29 Jul 2023 21:41:10 -0700 Subject: [PATCH] add support for vs_module_defs to Executables fixes: #9254 --- .../snippets/executable_vs_module_defs.md | 4 ++ docs/yaml/functions/executable.yaml | 10 +++++ mesonbuild/backend/ninjabackend.py | 2 + mesonbuild/backend/vs2010backend.py | 2 +- mesonbuild/build.py | 44 +++++++++++-------- mesonbuild/interpreter/kwargs.py | 1 + mesonbuild/interpreter/type_checking.py | 1 + .../9 vs module defs generated/exe.def | 2 + .../9 vs module defs generated/meson.build | 2 +- .../windows/9 vs module defs generated/prog.c | 6 ++- 10 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 docs/markdown/snippets/executable_vs_module_defs.md create mode 100644 test cases/windows/9 vs module defs generated/exe.def diff --git a/docs/markdown/snippets/executable_vs_module_defs.md b/docs/markdown/snippets/executable_vs_module_defs.md new file mode 100644 index 000000000000..79017488d09f --- /dev/null +++ b/docs/markdown/snippets/executable_vs_module_defs.md @@ -0,0 +1,4 @@ +## Executable gains vs_module_defs keyword + +This allows using a .def file to control which functions an [[executable]] will +expose to a [[shared_module]]. diff --git a/docs/yaml/functions/executable.yaml b/docs/yaml/functions/executable.yaml index cdf764a07387..9dc58e04749c 100644 --- a/docs/yaml/functions/executable.yaml +++ b/docs/yaml/functions/executable.yaml @@ -44,3 +44,13 @@ kwargs: type: bool since: 0.49.0 description: Build a position-independent executable. + + vs_module_defs: + type: str | file | custom_tgt | custom_idx + since: 1.3.0 + description: | + Specify a Microsoft module definition file for controlling symbol exports, + etc., on platforms where that is possible (e.g. Windows). + + This can be used to expose which functions a shared_module loaded by an + executable will be allowed to use diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 925941a965ac..04f3505f41b1 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3248,6 +3248,8 @@ def get_target_type_link_args(self, target, linker): commands += linker.gen_import_library_args(self.get_import_filename(target)) 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): if isinstance(target, build.SharedModule): commands += linker.get_std_shared_module_link_args(target.get_options()) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 0b6be1c79aad..fd19ff3d37cd 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1530,7 +1530,7 @@ def add_non_makefile_vcxproj_elements( # DLLs built with MSVC always have an import library except when # they're data-only DLLs, but we don't support those yet. ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename() - if isinstance(target, build.SharedLibrary): + if isinstance(target, (build.SharedLibrary, build.Executable)): # Add module definitions file, if provided if target.vs_module_defs: relpath = os.path.join(down, target.vs_module_defs.rel_to_builddir(self.build_to_src)) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index e83dcab539f4..b8287498ecf6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -114,7 +114,7 @@ rust_kwargs | cs_kwargs) -known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'} +known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs'} 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'} @@ -1702,6 +1702,26 @@ def check_module_linking(self): 'use shared_library() with `override_options: [\'b_lundef=false\']` instead.') link_target.force_soname = True + def process_vs_module_defs_kw(self, kwargs: T.Dict[str, T.Any]) -> None: + if kwargs.get('vs_module_defs') is not None: + path: T.Union[str, File, CustomTarget, CustomTargetIndex] = kwargs['vs_module_defs'] + if isinstance(path, str): + if os.path.isabs(path): + self.vs_module_defs = File.from_absolute_file(path) + else: + self.vs_module_defs = File.from_source_file(self.environment.source_dir, self.subdir, path) + elif isinstance(path, File): + # When passing a generated file. + self.vs_module_defs = path + elif isinstance(path, (CustomTarget, CustomTargetIndex)): + # When passing output of a Custom Target + self.vs_module_defs = File.from_built_file(path.get_subdir(), path.get_filename()) + else: + raise InvalidArguments( + 'vs_module_defs must be either a string, ' + 'a file object, a Custom Target, or a Custom Target Index') + self.process_link_depends(path) + class FileInTargetPrivateDir: """Represents a file with the path '/path/to/build/target_private_dir/fname'. target_private_dir is the return value of get_target_private_dir which is e.g. 'subdir/target.p'. @@ -1925,6 +1945,9 @@ def __init__( # Remember that this exe was returned by `find_program()` through an override self.was_returned_by_find_program = False + self.vs_module_defs: T.Optional[File] = None + self.process_vs_module_defs_kw(kwargs) + def post_init(self) -> None: super().post_init() machine = self.environment.machines[self.for_machine] @@ -2323,24 +2346,7 @@ def process_kwargs(self, kwargs): self.darwin_versions = (self.soversion, self.soversion) # Visual Studio module-definitions file - if kwargs.get('vs_module_defs') is not None: - path = kwargs['vs_module_defs'] - if isinstance(path, str): - if os.path.isabs(path): - self.vs_module_defs = File.from_absolute_file(path) - else: - self.vs_module_defs = File.from_source_file(self.environment.source_dir, self.subdir, path) - elif isinstance(path, File): - # When passing a generated file. - self.vs_module_defs = path - elif isinstance(path, (CustomTarget, CustomTargetIndex)): - # When passing output of a Custom Target - self.vs_module_defs = File.from_built_file(path.get_subdir(), path.get_filename()) - else: - raise InvalidArguments( - 'Shared library vs_module_defs must be either a string, ' - 'a file object, a Custom Target, or a Custom Target Index') - self.process_link_depends(path) + self.process_vs_module_defs_kw(kwargs) rust_abi = kwargs.get('rust_abi') rust_crate_type = kwargs.get('rust_crate_type') diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 9a8d05aff2f6..defc93fbeca7 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -335,6 +335,7 @@ class _BuildTarget(_BaseBuildTarget): class Executable(_BuildTarget): gui_app: T.Optional[bool] + vs_module_defs: T.Optional[T.Union[str, File, build.CustomTarget, build.CustomTargetIndex]] win_subsystem: T.Optional[str] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index f14046084e51..717f0ccb4f9f 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -595,6 +595,7 @@ def _convert_darwin_versions(val: T.List[T.Union[str, int]]) -> T.Optional[T.Tup EXECUTABLE_KWS = [ *_BUILD_TARGET_KWS, *_EXCLUSIVE_EXECUTABLE_KWS, + _VS_MODULE_DEFS_KW.evolve(since='1.3.0', since_values=None), ] # Arguments exclusive to library types diff --git a/test cases/windows/9 vs module defs generated/exe.def b/test cases/windows/9 vs module defs generated/exe.def new file mode 100644 index 000000000000..9031a843dc61 --- /dev/null +++ b/test cases/windows/9 vs module defs generated/exe.def @@ -0,0 +1,2 @@ +EXPORTS + exefunc diff --git a/test cases/windows/9 vs module defs generated/meson.build b/test cases/windows/9 vs module defs generated/meson.build index 7728ca77a050..fd064420d740 100644 --- a/test cases/windows/9 vs module defs generated/meson.build +++ b/test cases/windows/9 vs module defs generated/meson.build @@ -1,5 +1,5 @@ project('generated_dll_module_defs', 'c') subdir('subdir') -exe = executable('prog', 'prog.c', link_with : shlib) +exe = executable('prog', 'prog.c', link_with : shlib, vs_module_defs : 'exe.def') test('runtest', exe) diff --git a/test cases/windows/9 vs module defs generated/prog.c b/test cases/windows/9 vs module defs generated/prog.c index 066ac227a717..4466a8c85935 100644 --- a/test cases/windows/9 vs module defs generated/prog.c +++ b/test cases/windows/9 vs module defs generated/prog.c @@ -1,5 +1,9 @@ int somedllfunc(void); +int exefunc(void) { + return 42; +} + int main(void) { - return somedllfunc() == 42 ? 0 : 1; + return somedllfunc() == exefunc() ? 0 : 1; }