Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add win_subsystem kwarg. Closes #7765. #7791

Merged
merged 1 commit into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docs/markdown/Reference-manual.md
Original file line number Diff line number Diff line change
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear if numeric values are expected to work here, so perhaps is a string specifying the subsystem...?

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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 []
jpakkane marked this conversation as resolved.
Show resolved Hide resolved

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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
jpakkane marked this conversation as resolved.
Show resolved Hide resolved

#
# 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'])