qt: add preserve_paths keyword to functions

This allow to generate ui and moc under subdirectories,
as this is allowed with generic generators.
pull/12839/head
Charles Brunet 2 years ago committed by Dylan Baker
parent 6c2c4612cc
commit f4f50db441
  1. 17
      docs/markdown/Qt6-module.md
  2. 15
      docs/markdown/_include_qt_base.md
  3. 10
      docs/markdown/snippets/qt_preserve_path_from.md
  4. 38
      mesonbuild/modules/qt.py
  5. 2
      test cases/frameworks/4 qt/mainWindow.h
  6. 10
      test cases/frameworks/4 qt/meson.build
  7. 0
      test cases/frameworks/4 qt/ui/mainWindow.ui

@ -39,6 +39,11 @@ It takes no positional arguments, and the following keyword arguments:
*New in 0.60.0*: support for custom_target, custom_target_index, and generator_output. *New in 0.60.0*: support for custom_target, custom_target_index, and generator_output.
- `extra_args` string[]: Extra arguments to pass directly to `qt-uic` - `extra_args` string[]: Extra arguments to pass directly to `qt-uic`
- `method` string: The method to use to detect Qt, see [[dependency]] - `method` string: The method to use to detect Qt, see [[dependency]]
- `preserve_paths` bool: *New in 1.4.0*. If `true`, specifies that the output
files need to maintain their directory structure inside the target temporary
directory. For instance, when a file called `subdir/one.input` is processed
it generates a file `{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).
## compile_moc ## compile_moc
@ -59,6 +64,11 @@ It takes no positional arguments, and the following keyword arguments:
- `dependencies`: dependency objects whose include directories are used by moc. - `dependencies`: dependency objects whose include directories are used by moc.
- `include_directories` (string | IncludeDirectory)[]: A list of `include_directory()` - `include_directories` (string | IncludeDirectory)[]: A list of `include_directory()`
objects used when transpiling the .moc files objects used when transpiling the .moc files
- `preserve_paths` bool: *New in 1.4.0*. If `true`, specifies that the output
files need to maintain their directory structure inside the target temporary
directory. For instance, when a file called `subdir/one.input` is processed
it generates a file `{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).
## preprocess ## preprocess
@ -96,7 +106,12 @@ This method takes the following keyword arguments:
- `dependencies` Dependency[]: dependency objects needed by moc. - `dependencies` Dependency[]: dependency objects needed by moc.
- *Deprecated in 0.59.0.*: `sources`: a list of extra sources, which are added - *Deprecated in 0.59.0.*: `sources`: a list of extra sources, which are added
to the output unchanged. to the output unchanged.
- `preserve_paths` bool: *Since 1.4.0*. If `true`, specifies that the output
files need to maintain their directory structure inside the target temporary
directory. For instance, when a file called `subdir/one.input` is processed
it generates a file `{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).
It returns an array of targets and sources to pass to a compilation target. It returns an array of targets and sources to pass to a compilation target.
## compile_translations ## compile_translations

@ -28,6 +28,11 @@ It takes no positional arguments, and the following keyword arguments:
- `extra_args` string[]: Extra arguments to pass directly to `qt-uic` - `extra_args` string[]: Extra arguments to pass directly to `qt-uic`
- `method` string: The method to use to detect Qt, see `dependency()` for more - `method` string: The method to use to detect Qt, see `dependency()` for more
information. information.
- `preserve_paths` bool: *Since 1.4.0*. If `true`, specifies that the output
files need to maintain their directory structure inside the target temporary
directory. For instance, when a file called `subdir/one.input` is processed
it generates a file `{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).
## compile_moc ## compile_moc
@ -49,6 +54,11 @@ It takes no positional arguments, and the following keyword arguments:
- `dependencies`: dependency objects whose include directories are used by moc. - `dependencies`: dependency objects whose include directories are used by moc.
- `include_directories` (string | IncludeDirectory)[]: A list of `include_directory()` - `include_directories` (string | IncludeDirectory)[]: A list of `include_directory()`
objects used when transpiling the .moc files objects used when transpiling the .moc files
- `preserve_paths` bool: *New in 1.4.0*. If `true`, specifies that the output
files need to maintain their directory structure inside the target temporary
directory. For instance, when a file called `subdir/one.input` is processed
it generates a file `{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).
## preprocess ## preprocess
@ -78,6 +88,11 @@ This method takes the following keyword arguments:
- `rcc_extra_arguments` string[]: any additional arguments to `rcc`. Since v0.49.0. - `rcc_extra_arguments` string[]: any additional arguments to `rcc`. Since v0.49.0.
- `dependencies` Dependency[]: dependency objects needed by moc. Available since v0.48.0. - `dependencies` Dependency[]: dependency objects needed by moc. Available since v0.48.0.
- `sources`: a list of extra sources, which are added to the output unchanged. Deprecated in 0.59.0. - `sources`: a list of extra sources, which are added to the output unchanged. Deprecated in 0.59.0.
- `preserve_paths` bool: *New in 1.4.0*. If `true`, specifies that the output
files need to maintain their directory structure inside the target temporary
directory. For instance, when a file called `subdir/one.input` is processed
it generates a file `{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).
It returns an array of targets and sources to pass to a compilation target. It returns an array of targets and sources to pass to a compilation target.

@ -0,0 +1,10 @@
## Added `preserve_paths` keyword argument to qt module functions.
In `qt4`, `qt5`, and `qt6` modules, `compile_ui`, `compile_moc`, and
`preprocess` functions now have a `preserve_paths` keyword argument.
If `'true`, it specifies that the output files need to maintain their directory
structure inside the target temporary directory. For instance, when a file
called `subdir/one.input` is processed it generates a file
`{target private directory}/subdir/one.out` when `true`,
and `{target private directory}/one.out` when `false` (default).

@ -48,6 +48,7 @@ if T.TYPE_CHECKING:
sources: T.Sequence[T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]] sources: T.Sequence[T.Union[FileOrString, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]
extra_args: T.List[str] extra_args: T.List[str]
method: str method: str
preserve_paths: bool
class MocCompilerKwArgs(TypedDict): class MocCompilerKwArgs(TypedDict):
@ -59,6 +60,7 @@ if T.TYPE_CHECKING:
method: str method: str
include_directories: T.List[T.Union[str, build.IncludeDirs]] include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[Dependency, ExternalLibrary]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
preserve_paths: bool
class PreprocessKwArgs(TypedDict): class PreprocessKwArgs(TypedDict):
@ -73,6 +75,7 @@ if T.TYPE_CHECKING:
include_directories: T.List[T.Union[str, build.IncludeDirs]] include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[Dependency, ExternalLibrary]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
method: str method: str
preserve_paths: bool
class HasToolKwArgs(kwargs.ExtractRequired): class HasToolKwArgs(kwargs.ExtractRequired):
@ -376,9 +379,10 @@ class QtBaseModule(ExtensionModule):
required=True, required=True,
), ),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('method', str, default='auto') KwargInfo('method', str, default='auto'),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
) )
def compile_ui(self, state: 'ModuleState', args: T.Tuple, kwargs: 'UICompilerKwArgs') -> ModuleReturnValue: def compile_ui(self, state: ModuleState, args: T.Tuple, kwargs: UICompilerKwArgs) -> ModuleReturnValue:
"""Compile UI resources into cpp headers.""" """Compile UI resources into cpp headers."""
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['sources']): if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['sources']):
FeatureNew.single_use('qt.compile_ui: custom_target or generator for "sources" keyword argument', FeatureNew.single_use('qt.compile_ui: custom_target or generator for "sources" keyword argument',
@ -386,7 +390,7 @@ class QtBaseModule(ExtensionModule):
out = self._compile_ui_impl(state, kwargs) out = self._compile_ui_impl(state, kwargs)
return ModuleReturnValue(out, [out]) return ModuleReturnValue(out, [out])
def _compile_ui_impl(self, state: 'ModuleState', kwargs: 'UICompilerKwArgs') -> build.GeneratedList: def _compile_ui_impl(self, state: ModuleState, kwargs: UICompilerKwArgs) -> build.GeneratedList:
# Avoid the FeatureNew when dispatching from preprocess # Avoid the FeatureNew when dispatching from preprocess
self._detect_tools(state, kwargs['method']) self._detect_tools(state, kwargs['method'])
if not self.tools['uic'].found(): if not self.tools['uic'].found():
@ -394,13 +398,14 @@ class QtBaseModule(ExtensionModule):
"please check your qt{2} installation") "please check your qt{2} installation")
raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version)) raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version))
preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None
# TODO: This generator isn't added to the generator list in the Interpreter # TODO: This generator isn't added to the generator list in the Interpreter
gen = build.Generator( gen = build.Generator(
self.tools['uic'], self.tools['uic'],
kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'],
['ui_@BASENAME@.h'], ['ui_@BASENAME@.h'],
name=f'Qt{self.qt_version} ui') name=f'Qt{self.qt_version} ui')
return gen.process_files(kwargs['sources'], state) return gen.process_files(kwargs['sources'], state, preserve_path_from)
@FeatureNew('qt.compile_moc', '0.59.0') @FeatureNew('qt.compile_moc', '0.59.0')
@noPosargs @noPosargs
@ -422,8 +427,9 @@ class QtBaseModule(ExtensionModule):
KwargInfo('method', str, default='auto'), KwargInfo('method', str, default='auto'),
KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]), KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]), KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
) )
def compile_moc(self, state: 'ModuleState', args: T.Tuple, kwargs: 'MocCompilerKwArgs') -> ModuleReturnValue: def compile_moc(self, state: ModuleState, args: T.Tuple, kwargs: MocCompilerKwArgs) -> ModuleReturnValue:
if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['headers']): if any(isinstance(s, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for s in kwargs['headers']):
FeatureNew.single_use('qt.compile_moc: custom_target or generator for "headers" keyword argument', FeatureNew.single_use('qt.compile_moc: custom_target or generator for "headers" keyword argument',
'0.60.0', state.subproject, location=state.current_node) '0.60.0', state.subproject, location=state.current_node)
@ -433,7 +439,7 @@ class QtBaseModule(ExtensionModule):
out = self._compile_moc_impl(state, kwargs) out = self._compile_moc_impl(state, kwargs)
return ModuleReturnValue(out, [out]) return ModuleReturnValue(out, [out])
def _compile_moc_impl(self, state: 'ModuleState', kwargs: 'MocCompilerKwArgs') -> T.List[build.GeneratedList]: def _compile_moc_impl(self, state: ModuleState, kwargs: MocCompilerKwArgs) -> T.List[build.GeneratedList]:
# Avoid the FeatureNew when dispatching from preprocess # Avoid the FeatureNew when dispatching from preprocess
self._detect_tools(state, kwargs['method']) self._detect_tools(state, kwargs['method'])
if not self.tools['moc'].found(): if not self.tools['moc'].found():
@ -458,18 +464,19 @@ class QtBaseModule(ExtensionModule):
DEPFILE_ARGS: T.List[str] = ['--output-dep-file'] if self._moc_supports_depfiles else [] DEPFILE_ARGS: T.List[str] = ['--output-dep-file'] if self._moc_supports_depfiles else []
arguments = kwargs['extra_args'] + DEPFILE_ARGS + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@'] arguments = kwargs['extra_args'] + DEPFILE_ARGS + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@']
preserve_path_from = os.path.join(state.source_root, state.subdir) if kwargs['preserve_paths'] else None
if kwargs['headers']: if kwargs['headers']:
moc_gen = build.Generator( moc_gen = build.Generator(
self.tools['moc'], arguments, ['moc_@BASENAME@.cpp'], self.tools['moc'], arguments, ['moc_@BASENAME@.cpp'],
depfile='moc_@BASENAME@.cpp.d', depfile='moc_@BASENAME@.cpp.d',
name=f'Qt{self.qt_version} moc header') name=f'Qt{self.qt_version} moc header')
output.append(moc_gen.process_files(kwargs['headers'], state)) output.append(moc_gen.process_files(kwargs['headers'], state, preserve_path_from))
if kwargs['sources']: if kwargs['sources']:
moc_gen = build.Generator( moc_gen = build.Generator(
self.tools['moc'], arguments, ['@BASENAME@.moc'], self.tools['moc'], arguments, ['@BASENAME@.moc'],
depfile='@BASENAME@.moc.d', depfile='@BASENAME@.moc.d',
name=f'Qt{self.qt_version} moc source') name=f'Qt{self.qt_version} moc source')
output.append(moc_gen.process_files(kwargs['sources'], state)) output.append(moc_gen.process_files(kwargs['sources'], state, preserve_path_from))
return output return output
@ -487,8 +494,9 @@ class QtBaseModule(ExtensionModule):
KwargInfo('method', str, default='auto'), KwargInfo('method', str, default='auto'),
KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]), KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]), KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
KwargInfo('preserve_paths', bool, default=False, since='1.4.0'),
) )
def preprocess(self, state: 'ModuleState', args: T.List[T.Union[str, File]], kwargs: 'PreprocessKwArgs') -> ModuleReturnValue: def preprocess(self, state: ModuleState, args: T.List[T.Union[str, File]], kwargs: PreprocessKwArgs) -> ModuleReturnValue:
_sources = args[1:] _sources = args[1:]
if _sources: if _sources:
FeatureDeprecated.single_use('qt.preprocess positional sources', '0.59', state.subproject, location=state.current_node) FeatureDeprecated.single_use('qt.preprocess positional sources', '0.59', state.subproject, location=state.current_node)
@ -502,7 +510,7 @@ class QtBaseModule(ExtensionModule):
if kwargs['qresources']: if kwargs['qresources']:
# custom output name set? -> one output file, multiple otherwise # custom output name set? -> one output file, multiple otherwise
rcc_kwargs: 'ResourceCompilerKwArgs' = {'name': '', 'sources': kwargs['qresources'], 'extra_args': kwargs['rcc_extra_arguments'], 'method': method} rcc_kwargs: ResourceCompilerKwArgs = {'name': '', 'sources': kwargs['qresources'], 'extra_args': kwargs['rcc_extra_arguments'], 'method': method}
if args: if args:
name = args[0] name = args[0]
if not isinstance(name, str): if not isinstance(name, str):
@ -511,17 +519,23 @@ class QtBaseModule(ExtensionModule):
sources.extend(self._compile_resources_impl(state, rcc_kwargs)) sources.extend(self._compile_resources_impl(state, rcc_kwargs))
if kwargs['ui_files']: if kwargs['ui_files']:
ui_kwargs: 'UICompilerKwArgs' = {'sources': kwargs['ui_files'], 'extra_args': kwargs['uic_extra_arguments'], 'method': method} ui_kwargs: UICompilerKwArgs = {
'sources': kwargs['ui_files'],
'extra_args': kwargs['uic_extra_arguments'],
'method': method,
'preserve_paths': kwargs['preserve_paths'],
}
sources.append(self._compile_ui_impl(state, ui_kwargs)) sources.append(self._compile_ui_impl(state, ui_kwargs))
if kwargs['moc_headers'] or kwargs['moc_sources']: if kwargs['moc_headers'] or kwargs['moc_sources']:
moc_kwargs: 'MocCompilerKwArgs' = { moc_kwargs: MocCompilerKwArgs = {
'extra_args': kwargs['moc_extra_arguments'], 'extra_args': kwargs['moc_extra_arguments'],
'sources': kwargs['moc_sources'], 'sources': kwargs['moc_sources'],
'headers': kwargs['moc_headers'], 'headers': kwargs['moc_headers'],
'include_directories': kwargs['include_directories'], 'include_directories': kwargs['include_directories'],
'dependencies': kwargs['dependencies'], 'dependencies': kwargs['dependencies'],
'method': method, 'method': method,
'preserve_paths': kwargs['preserve_paths'],
} }
sources.extend(self._compile_moc_impl(state, moc_kwargs)) sources.extend(self._compile_moc_impl(state, moc_kwargs))

@ -3,7 +3,7 @@
#include <QObject> #include <QObject>
#include <QMainWindow> #include <QMainWindow>
#include "ui_mainWindow.h" #include "ui/ui_mainWindow.h"
class NotificationModel; class NotificationModel;

@ -61,11 +61,15 @@ foreach qt : ['qt4', 'qt5', 'qt6']
method : get_option('method') method : get_option('method')
) )
# XML files that need to be compiled with the uic tol. # XML files that need to be compiled with the uic tol.
prep += qtmodule.compile_ui(sources : 'mainWindow.ui', method: get_option('method')) prep += qtmodule.compile_ui(
sources : 'ui/mainWindow.ui',
method: get_option('method'),
preserve_paths: true)
qtmodule.preprocess( qtmodule.preprocess(
ui_files : 'mainWindow.ui', ui_files : 'ui/mainWindow.ui',
method: get_option('method')) method: get_option('method'),
preserve_paths: true)
# Resource file(s) for rcc compiler # Resource file(s) for rcc compiler
extra_cpp_args = [] extra_cpp_args = []

Loading…
Cancel
Save