Add win_subsystem kwarg. Closes #7765.

pull/7760/head
Jussi Pakkanen 4 years ago
parent 8b20852b0f
commit 1a0603835e
  1. 11
      docs/markdown/Reference-manual.md
  2. 22
      docs/markdown/snippets/winsubsystem.md
  3. 5
      mesonbuild/backend/ninjabackend.py
  4. 9
      mesonbuild/backend/vs2010backend.py
  5. 23
      mesonbuild/build.py
  6. 7
      mesonbuild/compilers/compilers.py
  7. 14
      mesonbuild/compilers/mixins/gnu.py
  8. 3
      mesonbuild/compilers/mixins/visualstudio.py
  9. 11
      test cases/windows/16 gui app/meson.build

@ -599,8 +599,9 @@ be passed to [shared and static libraries](#library).
- `extra_files`: not used for the build itself but are shown as - `extra_files`: not used for the build itself but are shown as
source files in IDEs that group files by targets (such as Visual source files in IDEs that group files by targets (such as Visual
Studio) Studio)
- `gui_app`: when set to true flags this target as a GUI application on - `gui_app`: when set to true flags this target as a GUI application
platforms where this makes a difference (e.g. Windows). 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 - `link_args`: flags to use during linking. You can use UNIX-style
flags here for all platforms. flags here for all platforms.
- `link_depends`: strings, files, or custom targets the link step - `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 - `pie` *(since 0.49.0)*: build a position-independent executable
- `native`: is a boolean controlling whether the target is compiled for the - `native`: is a boolean controlling whether the target is compiled for the
build or host machines. Defaults to false, building for the host machine. 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 The list of `sources`, `objects`, and `dependencies` is always
flattened, which means you can freely nest and add lists while flattened, which means you can freely nest and add lists while

@ -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')
```

@ -2587,7 +2587,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
# If gui_app is significant on this platform, add the appropriate linker arguments. # 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 # 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. # 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 return commands
def get_link_whole_args(self, linker, target): def get_link_whole_args(self, linker, target):

@ -754,8 +754,13 @@ class Vs2010Backend(backends.Backend):
self.handled_target_deps[target.get_id()] = [] self.handled_target_deps[target.get_id()] = []
if isinstance(target, build.Executable): if isinstance(target, build.Executable):
conftype = 'Application' conftype = 'Application'
if not target.gui_app: if target.gui_app is not None:
subsystem = 'Console' 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): elif isinstance(target, build.StaticLibrary):
conftype = 'StaticLibrary' conftype = 'StaticLibrary'
elif isinstance(target, build.SharedLibrary): elif isinstance(target, build.SharedLibrary):

@ -88,6 +88,7 @@ buildtarget_kwargs = set([
'sources', 'sources',
'gnu_symbol_visibility', 'gnu_symbol_visibility',
'link_language', 'link_language',
'win_subsystem',
]) ])
known_build_target_kwargs = ( known_build_target_kwargs = (
@ -924,11 +925,21 @@ This will become a hard error in a future Meson release.''')
raise InvalidArguments('Main class must be a string') raise InvalidArguments('Main class must be a string')
self.main_class = main_class self.main_class = main_class
if isinstance(self, Executable): if isinstance(self, Executable):
self.gui_app = kwargs.get('gui_app', False) # This kwarg is deprecated. The value of "none" means that the kwarg
if not isinstance(self.gui_app, bool): # was not specified and win_subsystem should be used instead.
raise InvalidArguments('Argument gui_app must be boolean.') self.gui_app = None
if 'gui_app' in kwargs:
mlog.deprecation('The gui_app kwarg is deprecated, use win_subsystem instead.')
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: elif 'gui_app' in kwargs:
raise InvalidArguments('Argument gui_app can only be used on executables.') 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') extra_files = extract_as_list(kwargs, 'extra_files')
for i in extra_files: for i in extra_files:
assert(isinstance(i, File)) assert(isinstance(i, File))
@ -1000,6 +1011,12 @@ This will become a hard error in a future Meson release.''')
if self.gnu_symbol_visibility not in permitted: if self.gnu_symbol_visibility not in permitted:
raise InvalidArguments('GNU symbol visibility arg {} not one of: {}'.format(self.symbol_visibility, ', '.join(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): def _extract_pic_pie(self, kwargs, arg):
# Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags
all_flags = self.extra_args['c'] + self.extra_args['cpp'] all_flags = self.extra_args['c'] + self.extra_args['cpp']

@ -878,6 +878,13 @@ class Compiler(metaclass=abc.ABCMeta):
def get_gui_app_args(self, value: bool) -> T.List[str]: def get_gui_app_args(self, value: bool) -> T.List[str]:
return [] 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]: def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
raise EnvironmentException( raise EnvironmentException(
'Language {} does not support function attributes.'.format(self.get_display_language())) 'Language {} does not support function attributes.'.format(self.get_display_language()))

@ -219,6 +219,20 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
return ['-mwindows' if value else '-mconsole'] return ['-mwindows' if value else '-mconsole']
return [] 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 args
def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]: 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): for idx, i in enumerate(parameter_list):
if i[:2] == '-I' or i[:2] == '-L': if i[:2] == '-I' or i[:2] == '-L':

@ -205,6 +205,9 @@ class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
else: else:
return ['/SUBSYSTEM:CONSOLE'] 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]: def get_pic_args(self) -> T.List[str]:
return [] # PIC is handled by the loader on Windows return [] # PIC is handled by the loader on Windows

@ -6,16 +6,21 @@ project('gui_app_test', 'c')
# #
console_lib = static_library('main', 'console_prog.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:) # 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')
console_prog = executable('console_prog', 'console_prog.c', gui_app: false) gui_prog2 = executable('gui_prog2', 'gui_prog.c', gui_app: true)
console_prog = executable('console_prog', 'console_prog.c', win_subsystem: 'console')
console_prog2 = executable('console_prog2', 'console_prog.c', gui_app: false)
tester = find_program('gui_app_tester.py') tester = find_program('gui_app_tester.py')
test('is_gui', tester, args: [gui_prog, '2']) test('is_gui', tester, args: [gui_prog, '2'])
test('is_gui2', tester, args: [gui_prog2, '2'])
test('not_gui', tester, args: [console_prog, '3']) test('not_gui', tester, args: [console_prog, '3'])
test('not_gui2', tester, args: [console_prog2, '3'])

Loading…
Cancel
Save