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..f8ea81795556 --- /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 SharedModule. 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/docs/yaml/functions/shared_library.yaml b/docs/yaml/functions/shared_library.yaml index 956fb2cb2ce1..0105e40695be 100644 --- a/docs/yaml/functions/shared_library.yaml +++ b/docs/yaml/functions/shared_library.yaml @@ -44,3 +44,5 @@ kwargs: description: | Specify a Microsoft module definition file for controlling symbol exports, etc., on platforms where that is possible (e.g. Windows). + + *(Since 1.3.0)* indexes of custom_target are supported diff --git a/docs/yaml/functions/shared_module.yaml b/docs/yaml/functions/shared_module.yaml index 20bd5c48802c..b82c7eb0a240 100644 --- a/docs/yaml/functions/shared_module.yaml +++ b/docs/yaml/functions/shared_module.yaml @@ -39,3 +39,5 @@ kwargs: description: | Specify a Microsoft module definition file for controlling symbol exports, etc., on platforms where that is possible (e.g. Windows). + + *(Since 1.3.0)* indexes of custom_target are supported diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3ddc197aece7..e747d8368219 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -3250,6 +3250,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 0a382be22551..20ee36ebaed9 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -1575,7 +1575,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 24d2a7fa451a..3e1139dc9d6c 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'} known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs'} known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink'} @@ -1688,6 +1688,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( + 'Shared library vs_module_defs must be either a string, ' + 'a file object or a Custom Target') + 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'. @@ -1911,6 +1931,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] @@ -2373,24 +2396,7 @@ def process_kwargs(self, kwargs): self.darwin_versions = 2 * [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 or a Custom Target') - self.process_link_depends(path) + self.process_vs_module_defs_kw(kwargs) if 'rust_crate_type' in kwargs: rust_crate_type = kwargs['rust_crate_type'] diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index b77c5253e963..ceb982c695a9 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -329,6 +329,7 @@ class _BuildTarget(_BaseBuildTarget): class Executable(_BuildTarget): gui_app: T.Optional[bool] + vs_module_def: 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 c39916abb2b8..0e4503d69276 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -519,6 +519,7 @@ def _validate_win_subsystem(value: T.Optional[str]) -> T.Optional[str]: EXECUTABLE_KWS = [ *_BUILD_TARGET_KWS, *_EXCLUSIVE_EXECUTABLE_KWS, + _VS_MODULE_DEF_KW.evolve(since='1.3.0', since_values=None), ] # Arguments exclusive to StaticLibrary. These are separated to make integrating 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..869ca5d9bc85 --- /dev/null +++ b/test cases/windows/9 vs module defs generated/exe.def @@ -0,0 +1,2 @@ +EXPORTS + thisfunc 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; }