Skip to content

Commit

Permalink
Add win_subsystem kwarg. Closes #7765.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpakkane committed Oct 7, 2020
1 parent 8b20852 commit 1a06038
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 11 deletions.
11 changes: 9 additions & 2 deletions docs/markdown/Reference-manual.md
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions 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')
```
5 changes: 4 additions & 1 deletion mesonbuild/backend/ninjabackend.py
Expand Up @@ -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):
Expand Down
9 changes: 7 additions & 2 deletions mesonbuild/backend/vs2010backend.py
Expand Up @@ -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):
Expand Down
23 changes: 20 additions & 3 deletions mesonbuild/build.py
Expand Up @@ -88,6 +88,7 @@
'sources',
'gnu_symbol_visibility',
'link_language',
'win_subsystem',
])

known_build_target_kwargs = (
Expand Down Expand Up @@ -924,11 +925,21 @@ 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:
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:
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))
Expand Down Expand Up @@ -1000,6 +1011,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']
Expand Down
7 changes: 7 additions & 0 deletions mesonbuild/compilers/compilers.py
Expand Up @@ -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()))
Expand Down
14 changes: 14 additions & 0 deletions mesonbuild/compilers/mixins/gnu.py
Expand Up @@ -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 args


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':
Expand Down
3 changes: 3 additions & 0 deletions mesonbuild/compilers/mixins/visualstudio.py
Expand Up @@ -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

Expand Down
11 changes: 8 additions & 3 deletions test cases/windows/16 gui app/meson.build
Expand Up @@ -6,16 +6,21 @@ 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)
console_prog = executable('console_prog', 'console_prog.c', gui_app: false)
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', win_subsystem: 'console')
console_prog2 = executable('console_prog2', 'console_prog.c', gui_app: false)

tester = find_program('gui_app_tester.py')

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_gui2', tester, args: [console_prog2, '3'])

0 comments on commit 1a06038

Please sign in to comment.