From 6b66c4c913211f08d77b92128bf866dd34c37e32 Mon Sep 17 00:00:00 2001 From: Jussi Pakkanen Date: Sun, 27 Sep 2020 20:21:59 +0300 Subject: [PATCH] Add win_subsystem kwarg. Closes #7765. --- docs/markdown/Reference-manual.md | 11 +++++++++-- docs/markdown/snippets/winsubsystem.md | 22 +++++++++++++++++++++ mesonbuild/backend/ninjabackend.py | 5 ++++- mesonbuild/backend/vs2010backend.py | 9 +++++++-- mesonbuild/build.py | 22 ++++++++++++++++++--- mesonbuild/compilers/compilers.py | 7 +++++++ mesonbuild/compilers/mixins/gnu.py | 14 +++++++++++++ mesonbuild/compilers/mixins/visualstudio.py | 3 +++ test cases/windows/16 gui app/meson.build | 6 ++++-- 9 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 docs/markdown/snippets/winsubsystem.md diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 8bff4282cbf6..c1e509ecd812 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -599,8 +599,9 @@ be passed to [shared and static libraries](#library). - `extra_files`: not used for the build itself but are shown as source files in IDEs that group files by targets (such as Visual Studio) -- `gui_app`: when set to true flags this target as a GUI application on - platforms where this makes a difference (e.g. Windows). +- `gui_app`: when set to true flags this target as a GUI application + on platforms where this makes a differerence, **deprecated** since + 0.56.0, use `win_subsystem` instead. - `link_args`: flags to use during linking. You can use UNIX-style flags here for all platforms. - `link_depends`: strings, files, or custom targets the link step @@ -677,6 +678,12 @@ be passed to [shared and static libraries](#library). - `pie` *(since 0.49.0)*: build a position-independent executable - `native`: is a boolean controlling whether the target is compiled for the build or host machines. Defaults to false, building for the host machine. +- `win_subsystem` *(since 0.56.0)* specifies the subsystem type to use + on the Windows platform. Typical values include `console` for text + mode programs and `windows` for gui apps. The value can also contain + version specification such as `windows,6.0'. See [MSDN + documentation](https://docs.microsoft.com/en-us/cpp/build/reference/subsystem-specify-subsystem) + for the full list. The default value is `console`. The list of `sources`, `objects`, and `dependencies` is always flattened, which means you can freely nest and add lists while diff --git a/docs/markdown/snippets/winsubsystem.md b/docs/markdown/snippets/winsubsystem.md new file mode 100644 index 000000000000..a7d589fe9321 --- /dev/null +++ b/docs/markdown/snippets/winsubsystem.md @@ -0,0 +1,22 @@ +## Add support for all Windows subsystem types + +It is now possible to build things like Windows kernel drivers with +the new `win_subsystem` keyword argument. This replaces the old +`gui_app` keyword argument, which is now deprecated. You should update +your project to use the new style like this: + +```meson +# Old way +executable(..., gui_app: 'true') +# New way +executable(..., win_subsystem: 'windows') +``` + +The argument supports versioning [as described on MSDN +documentation](https://docs.microsoft.com/en-us/cpp/build/reference/subsystem-specify-subsystem). +Thus to build a Windows kernel driver with a specific version you'd +write something like this: + +```meson +executable(..., win_subsystem: 'native,6.02') +``` diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index f816b1d44c8c..2ffbb85db7ff 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2587,7 +2587,10 @@ def get_target_type_link_args_post_dependencies(self, target, linker): # If gui_app is significant on this platform, add the appropriate linker arguments. # Unfortunately this can't be done in get_target_type_link_args, because some misguided # libraries (such as SDL2) add -mwindows to their link flags. - commands += linker.get_gui_app_args(target.gui_app) + if target.gui_app is not None: + commands += linker.get_gui_app_args(target.gui_app) + else: + commands += linker.get_win_subsystem_args(target.win_subsystem) return commands def get_link_whole_args(self, linker, target): diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 86ddc1d9bb5e..08360d3de907 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -754,8 +754,13 @@ def gen_vcxproj(self, target, ofname, guid): self.handled_target_deps[target.get_id()] = [] if isinstance(target, build.Executable): conftype = 'Application' - if not target.gui_app: - subsystem = 'Console' + if target.gui_app is not None: + if not target.gui_app: + subsystem = 'Console' + else: + # If someone knows how to set the version properly, + # please send a patch. + subsystem = target.win_subsystem.split(',')[0] elif isinstance(target, build.StaticLibrary): conftype = 'StaticLibrary' elif isinstance(target, build.SharedLibrary): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index be9763489d97..bb61e5fa52ef 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -88,6 +88,7 @@ 'sources', 'gnu_symbol_visibility', 'link_language', + 'win_subsystem', ]) known_build_target_kwargs = ( @@ -924,11 +925,20 @@ def process_kwargs(self, kwargs, environment): raise InvalidArguments('Main class must be a string') self.main_class = main_class if isinstance(self, Executable): - self.gui_app = kwargs.get('gui_app', False) - if not isinstance(self.gui_app, bool): - raise InvalidArguments('Argument gui_app must be boolean.') + # This kwarg is deprecated. The value of "none" means that the kwarg + # was not specified and win_subsystem should be used instead. + self.gui_app = None + if 'gui_app' in kwargs: + if 'win_subsystem' in kwargs: + raise InvalidArguments('Can specify only gui_app or win_subsystem for a target, not both.') + self.gui_app = kwargs['gui_app'] + if not isinstance(self.gui_app, bool): + raise InvalidArguments('Argument gui_app must be boolean.') + self.win_subsystem = self.validate_win_subsystem(kwargs.get('win_subsystem', 'console')) elif 'gui_app' in kwargs: raise InvalidArguments('Argument gui_app can only be used on executables.') + elif 'win_subsystem' in kwargs: + raise InvalidArguments('Argument win_subsystem can only be used on executables.') extra_files = extract_as_list(kwargs, 'extra_files') for i in extra_files: assert(isinstance(i, File)) @@ -1000,6 +1010,12 @@ def process_kwargs(self, kwargs, environment): if self.gnu_symbol_visibility not in permitted: raise InvalidArguments('GNU symbol visibility arg {} not one of: {}'.format(self.symbol_visibility, ', '.join(permitted))) + def validate_win_subsystem(self, value: str) -> str: + value = value.lower() + if re.fullmatch(r'(boot_application|console|efi_application|efi_boot_service_driver|efi_rom|efi_runtime_driver|native|posix|windows)(,\d+(\.\d+)?)?', value) is None: + raise InvalidArguments('Invalid value for win_subsystem: {}.'.format(value)) + return value + def _extract_pic_pie(self, kwargs, arg): # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags all_flags = self.extra_args['c'] + self.extra_args['cpp'] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 4b48e31463d9..fa1046a61cc0 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -878,6 +878,13 @@ def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]: def get_gui_app_args(self, value: bool) -> T.List[str]: return [] + def get_win_subsystem_args(self, value: str) -> T.List[str]: + # This returns an empty array rather than throws to simplify the code. + # Otherwise we would have to check whenever calling this function whether + # the target is for Windows. There are also many cases where this is + # a meaningless choice, such as with Jave or C#. + return [] + def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]: raise EnvironmentException( 'Language {} does not support function attributes.'.format(self.get_display_language())) diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index 41afadd1ef69..22c21d4ff617 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -219,6 +219,20 @@ def get_gui_app_args(self, value: bool) -> T.List[str]: return ['-mwindows' if value else '-mconsole'] return [] + def get_win_subsystem_args(self, value: str) -> T.List[str]: + args = [] + if self.info.is_windows() or self.info.is_cygwin(): + if 'windows' in value: + args = ['-Wl,--subsystem,windows'] + elif 'console' in value: + args = ['-Wl,--subsystem,console'] + else: + raise mesonlib.MesonException('Only "windows" and "console" are supported for win_subsystem with MinGW, not "{}".'.format(value)) + if ',' in value: + args[-1] = args[-1] + ':' + value.split(',')[1] + return [] + + def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]: for idx, i in enumerate(parameter_list): if i[:2] == '-I' or i[:2] == '-L': diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py index 3494beefeabc..75ab635e1700 100644 --- a/mesonbuild/compilers/mixins/visualstudio.py +++ b/mesonbuild/compilers/mixins/visualstudio.py @@ -205,6 +205,9 @@ def get_gui_app_args(self, value: bool) -> T.List[str]: else: return ['/SUBSYSTEM:CONSOLE'] + def get_win_subsystem_args(self, value: str) -> T.List[str]: + return ['/SUBSYSTEM:' + value.upper()] + def get_pic_args(self) -> T.List[str]: return [] # PIC is handled by the loader on Windows diff --git a/test cases/windows/16 gui app/meson.build b/test cases/windows/16 gui app/meson.build index 157055532f48..848d99c41143 100644 --- a/test cases/windows/16 gui app/meson.build +++ b/test cases/windows/16 gui app/meson.build @@ -6,13 +6,15 @@ project('gui_app_test', 'c') # console_lib = static_library('main', 'console_prog.c') -executable('console', 'dummy.c', link_with: console_lib, gui_app: false) +executable('console', 'dummy.c', link_with: console_lib, win_subsystem: 'console') +executable('console2', 'dummy.c', link_with: console_lib, gui_app: false) # # also verify that the correct subsystem is set by executable(gui_app:) # -gui_prog = executable('gui_prog', 'gui_prog.c', gui_app: true) +gui_prog = executable('gui_prog', 'gui_prog.c', win_subsystem: 'windows,6.0') +gui_prog2 = executable('gui_prog2', 'gui_prog.c', gui_app: true) console_prog = executable('console_prog', 'console_prog.c', gui_app: false) tester = find_program('gui_app_tester.py')