From f8604a912881e3c1b2555a576d433849d9b0c848 Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sat, 28 Apr 2018 14:53:24 +0200 Subject: [PATCH 1/8] [Qt module] Privates headers: Added failing test case Meson doesn't provides yet a convenient way to include private Qt headers Signed-off-by: Alexis Jeandet --- test cases/frameworks/4 qt/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test cases/frameworks/4 qt/main.cpp b/test cases/frameworks/4 qt/main.cpp index 388467e45..0fcc73430 100644 --- a/test cases/frameworks/4 qt/main.cpp +++ b/test cases/frameworks/4 qt/main.cpp @@ -1,5 +1,11 @@ #include #include "mainWindow.h" +// include some random private headers +#if QT_VERSION < 0x050000 + #include +#else + #include +#endif int main(int argc, char **argv) { #ifndef UNITY_BUILD From 2fc0a11062757c7f621b53d6ccfc158889c18544 Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sat, 28 Apr 2018 17:14:31 +0200 Subject: [PATCH 2/8] [Qt module] Privates headers: Implemented private_headers option This commit adds private_headers option in dependency method which tells QtDependency to add private headers include path to build flags. Since there is no easy way to do this with pkg-config only qmake method supports this, so with private_headers set qmake will always be used. Signed-off-by: Alexis Jeandet --- mesonbuild/dependencies/ui.py | 40 ++++++++++++++++++++++++-- mesonbuild/interpreter.py | 2 +- test cases/frameworks/4 qt/main.cpp | 4 +-- test cases/frameworks/4 qt/meson.build | 2 +- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 2f31196e4..70d2abc36 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -158,9 +158,8 @@ class QtBaseDependency(ExternalDependency): self.qtpkgname = self.qtname self.root = '/usr' self.bindir = None - mods = kwargs.get('modules', []) - if isinstance(mods, str): - mods = [mods] + self.private_headers = kwargs.get('private_headers', False) + mods = extract_as_list(kwargs, 'modules') if not mods: raise DependencyException('No ' + self.qtname + ' modules specified.') type_text = 'cross' if env.is_cross_build() else 'native' @@ -216,6 +215,11 @@ class QtBaseDependency(ExternalDependency): # qmake-based fallback if pkg-config fails. kwargs['required'] = False modules = OrderedDict() + # Until Qt's pkg-config files provide private headers path + # let's just fallback to qmake method + if self.private_headers: + self.is_found = False + return for module in mods: modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env, kwargs, language=self.language) @@ -296,6 +300,10 @@ class QtBaseDependency(ExternalDependency): for module in mods: mincdir = os.path.join(incdir, 'Qt' + module) self.compile_args.append('-I' + mincdir) + if self.private_headers: + priv_inc = self.get_private_includes(incdir, module) + for dir in priv_inc: + self.compile_args.append('-I' + dir) if for_windows(self.env.is_cross_build(), self.env): is_debug = self.env.cmd_line_options.buildtype.startswith('debug') dbg = 'd' if is_debug else '' @@ -361,6 +369,9 @@ class QtBaseDependency(ExternalDependency): # for you, patches are welcome. return compiler.get_pic_args() + def get_private_includes(self, incdir, module): + return tuple() + class Qt4Dependency(QtBaseDependency): def __init__(self, env, kwargs): @@ -386,6 +397,29 @@ class Qt5Dependency(QtBaseDependency): def get_pkgconfig_host_bins(self, core): return core.get_pkgconfig_variable('host_bins', {}) + def get_private_includes(self, incdir, module): + # usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private + # except for at least QtWebkit and Enginio where the module version doesn't match Qt version + # as an example with Qt 5.10.1 on linux you would get: + # /usr/include/qt5/QtCore/5.10.1/QtCore/private/ + # /usr/include/qt5/QtWidgets/5.10.1/QtWidgets/private/ + # /usr/include/qt5/Enginio/1.6.2/Enginio/private/ + + mod_inc_dir = os.path.join(incdir, 'Qt' + module) + private_dir = os.path.join(mod_inc_dir, self.version) + # fallback, let's try to find a directory with the latest version + if not os.path.exists(private_dir): + dirs = [filename for filename in os.listdir(mod_inc_dir) + if os.path.isdir(os.path.join(mod_inc_dir, filename))] + dirs.sort(reverse=True) + + for dirname in dirs: + if len(dirname.split('.')) == 3: + private_dir = dirname + break + return (private_dir, + os.path.join(private_dir, 'Qt' + module)) + # There are three different ways of depending on SDL2: # sdl2-config, pkg-config and OSX framework diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c94877823..0e48d6608 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1656,7 +1656,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'build_target': known_build_target_kwargs, 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install', 'format'}, 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'}, - 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'}, + 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version', 'private_headers'}, 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'}, 'executable': build.known_exe_kwargs, 'find_program': {'required', 'native'}, diff --git a/test cases/frameworks/4 qt/main.cpp b/test cases/frameworks/4 qt/main.cpp index 0fcc73430..0ad71540b 100644 --- a/test cases/frameworks/4 qt/main.cpp +++ b/test cases/frameworks/4 qt/main.cpp @@ -1,10 +1,10 @@ #include #include "mainWindow.h" // include some random private headers -#if QT_VERSION < 0x050000 +#if QT_VERSION > 0x050000 #include #else - #include + #include #endif int main(int argc, char **argv) { diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index e8d12b62e..16fe56408 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -33,7 +33,7 @@ foreach qt : ['qt4', 'qt5'] nocoredep = dependency(qt, modules : ['Gui'], required : qt == 'qt5', method : get_option('method')) # If qt4 modules are found, test that. qt5 is required. - qtdep = dependency(qt, modules : qt_modules, required : qt == 'qt5', method : get_option('method')) + qtdep = dependency(qt, modules : qt_modules, private_headers: true, required : qt == 'qt5', method : get_option('method')) if qtdep.found() qtmodule = import(qt) From 51868d00e7a25ef9e587228e949964a765ed9d2d Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sat, 28 Apr 2018 17:45:23 +0200 Subject: [PATCH 3/8] [Qt module] Privates headers: added documentation Signed-off-by: Alexis Jeandet --- docs/markdown/Qt5-module.md | 7 +++++++ docs/markdown/Reference-manual.md | 1 + 2 files changed, 8 insertions(+) diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md index b5393a85c..5a7a4c262 100644 --- a/docs/markdown/Qt5-module.md +++ b/docs/markdown/Qt5-module.md @@ -20,6 +20,7 @@ This method generates the necessary targets to build translation files with lrel - `install_dir` directory to install to (optional). - `build_by_default` when set to true, to have this target be built by default, that is, when invoking plain ninja; the default value is false (optional). +## Example A simple example would look like this: ```meson @@ -38,3 +39,9 @@ executable('myprog', 'main.cpp', 'myclass.cpp', moc_files, The 'modules' argument is used to include Qt modules in the project. See the Qt documentation for the [list of modules](http://doc.qt.io/qt-5/qtmodules.html). + +## private headers (since v0.47.0) + +Some projects needs Qt's private headers to build, that's why a **private_headers** keyword argument has been added to [dependency](Reference-manual.md#dependency) method. +Setting this optionnal argument will add private include path of the given module to the compiler flags. +Note that this option is only compatible with qmake dependency method, using auto or pkg-config will fallback to qmake. diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 776703c48..6bfee96dc 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -341,6 +341,7 @@ otherwise. This function supports the following keyword arguments: `>1.0.0`, `<=2.3.5` or `3.1.4` for exact matching. (*Added 0.37.0*) You can also specify multiple restrictions by passing a list to this keyword argument, such as: `['>=3.14.0', '<=4.1.0']`. +- `private_headers`, only available with Qt modules see [documentation](Qt5-module.md#private_headers). If dependency_name is '', the dependency is always not found. So with `required: false`, this always returns a dependency object for which the From 0045d95a16be18092adfb40a9a5df944bcb99aea Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sat, 28 Apr 2018 18:23:26 +0200 Subject: [PATCH 4/8] really switch to qmake automatically if pkg-config fails Signed-off-by: Alexis Jeandet --- mesonbuild/dependencies/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 70d2abc36..342b1dafb 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -173,7 +173,7 @@ class QtBaseDependency(ExternalDependency): if DependencyMethods.PKGCONFIG in self.methods: self._pkgconfig_detect(mods, kwargs) methods.append('pkgconfig') - if not self.is_found and DependencyMethods.QMAKE in self.methods: + if not self.is_found or DependencyMethods.QMAKE in self.methods: from_text = self._qmake_detect(mods, kwargs) methods.append('qmake-' + self.name) methods.append('qmake') From b380251305c24a5ce4c70b9c9861293fe12a470c Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sat, 28 Apr 2018 18:47:22 +0200 Subject: [PATCH 5/8] qconfig_p.h isn't available on OSX :( Signed-off-by: Alexis Jeandet --- test cases/frameworks/4 qt/main.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test cases/frameworks/4 qt/main.cpp b/test cases/frameworks/4 qt/main.cpp index 0ad71540b..e546d25fc 100644 --- a/test cases/frameworks/4 qt/main.cpp +++ b/test cases/frameworks/4 qt/main.cpp @@ -1,11 +1,8 @@ #include #include "mainWindow.h" // include some random private headers -#if QT_VERSION > 0x050000 - #include -#else - #include -#endif +#include + int main(int argc, char **argv) { #ifndef UNITY_BUILD From f784ee62eab752ca9b8c14281a0c6edbcde2c7b2 Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sat, 28 Apr 2018 23:36:05 +0200 Subject: [PATCH 6/8] Fixed private headers on OSX with framework stuff Removed Qt4 private headers test since it's hard to get Qt4 private headers installed on CI. Signed-off-by: Alexis Jeandet --- mesonbuild/dependencies/ui.py | 75 ++++++++++++++++++----------- test cases/frameworks/4 qt/main.cpp | 6 ++- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 342b1dafb..99346ec8d 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -147,6 +147,46 @@ class GnuStepDependency(ConfigToolDependency): return version +def _qt_get_private_includes(mod_inc_dir, module, qt_version): + # usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private + # except for at least QtWebkit and Enginio where the module version doesn't match Qt version + # as an example with Qt 5.10.1 on linux you would get: + # /usr/include/qt5/QtCore/5.10.1/QtCore/private/ + # /usr/include/qt5/QtWidgets/5.10.1/QtWidgets/private/ + # /usr/include/qt5/Enginio/1.6.2/Enginio/private/ + + # on Qt4 when available private folder is directly in module folder + if int(qt_version.split('.')[0]) < 5: + return tuple() + + private_dir = os.path.join(mod_inc_dir, qt_version) + # fallback, let's try to find a directory with the latest version + if not os.path.exists(private_dir): + dirs = [filename for filename in os.listdir(mod_inc_dir) + if os.path.isdir(os.path.join(mod_inc_dir, filename))] + dirs.sort(reverse=True) + + for dirname in dirs: + if len(dirname.split('.')) == 3: + private_dir = dirname + break + return (private_dir, + os.path.join(private_dir, 'Qt' + module)) + +class QtExtraFrameworkDependency(ExtraFrameworkDependency): + def __init__(self, name, required, path, env, lang, kwargs): + super().__init__(name, required, path, env, lang, kwargs) + self.mod_name = name[2:] + + def get_compile_args(self, with_private_headers=False, qt_version="0"): + if self.found(): + mod_inc_dir = os.path.join(self.path, self.name, 'Headers') + args = ['-I' + mod_inc_dir] + if with_private_headers: + args += ['-I' + dirname for dirname in _qt_get_private_includes(mod_inc_dir, self.mod_name, qt_version)] + return args + return [] + class QtBaseDependency(ExternalDependency): def __init__(self, name, env, kwargs): super().__init__(name, env, 'cpp', kwargs) @@ -301,7 +341,7 @@ class QtBaseDependency(ExternalDependency): mincdir = os.path.join(incdir, 'Qt' + module) self.compile_args.append('-I' + mincdir) if self.private_headers: - priv_inc = self.get_private_includes(incdir, module) + priv_inc = self.get_private_includes(mincdir, module) for dir in priv_inc: self.compile_args.append('-I' + dir) if for_windows(self.env.is_cross_build(), self.env): @@ -335,11 +375,12 @@ class QtBaseDependency(ExternalDependency): for m in modules: fname = 'Qt' + m - fwdep = ExtraFrameworkDependency(fname, False, libdir, self.env, - self.language, fw_kwargs) + fwdep = QtExtraFrameworkDependency(fname, False, libdir, self.env, + self.language, fw_kwargs) self.compile_args.append('-F' + libdir) if fwdep.found(): - self.compile_args += fwdep.get_compile_args() + self.compile_args += fwdep.get_compile_args(with_private_headers=self.private_headers, + qt_version=self.version) self.link_args += fwdep.get_link_args() else: break @@ -369,7 +410,7 @@ class QtBaseDependency(ExternalDependency): # for you, patches are welcome. return compiler.get_pic_args() - def get_private_includes(self, incdir, module): + def get_private_includes(self, mod_inc_dir, module): return tuple() @@ -397,28 +438,8 @@ class Qt5Dependency(QtBaseDependency): def get_pkgconfig_host_bins(self, core): return core.get_pkgconfig_variable('host_bins', {}) - def get_private_includes(self, incdir, module): - # usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private - # except for at least QtWebkit and Enginio where the module version doesn't match Qt version - # as an example with Qt 5.10.1 on linux you would get: - # /usr/include/qt5/QtCore/5.10.1/QtCore/private/ - # /usr/include/qt5/QtWidgets/5.10.1/QtWidgets/private/ - # /usr/include/qt5/Enginio/1.6.2/Enginio/private/ - - mod_inc_dir = os.path.join(incdir, 'Qt' + module) - private_dir = os.path.join(mod_inc_dir, self.version) - # fallback, let's try to find a directory with the latest version - if not os.path.exists(private_dir): - dirs = [filename for filename in os.listdir(mod_inc_dir) - if os.path.isdir(os.path.join(mod_inc_dir, filename))] - dirs.sort(reverse=True) - - for dirname in dirs: - if len(dirname.split('.')) == 3: - private_dir = dirname - break - return (private_dir, - os.path.join(private_dir, 'Qt' + module)) + def get_private_includes(self, mod_inc_dir, module): + return _qt_get_private_includes(mod_inc_dir, module, self.version) # There are three different ways of depending on SDL2: diff --git a/test cases/frameworks/4 qt/main.cpp b/test cases/frameworks/4 qt/main.cpp index e546d25fc..1efd165b5 100644 --- a/test cases/frameworks/4 qt/main.cpp +++ b/test cases/frameworks/4 qt/main.cpp @@ -1,8 +1,10 @@ #include #include "mainWindow.h" -// include some random private headers -#include +#if QT_VERSION > 0x050000 +// include some random private headers + #include +#endif int main(int argc, char **argv) { #ifndef UNITY_BUILD From f3a8efc11be3b356f95b39430d3bd165f1646038 Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Mon, 30 Apr 2018 11:39:58 +0200 Subject: [PATCH 7/8] Added Added Qt's private header support with pkg-config Just use the same approach than qmake to generate private headers path Signed-off-by: Alexis Jeandet --- docs/markdown/Qt5-module.md | 3 ++- mesonbuild/dependencies/ui.py | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md index 5a7a4c262..7b0bf2896 100644 --- a/docs/markdown/Qt5-module.md +++ b/docs/markdown/Qt5-module.md @@ -44,4 +44,5 @@ See the Qt documentation for the [list of modules](http://doc.qt.io/qt-5/qtmodul Some projects needs Qt's private headers to build, that's why a **private_headers** keyword argument has been added to [dependency](Reference-manual.md#dependency) method. Setting this optionnal argument will add private include path of the given module to the compiler flags. -Note that this option is only compatible with qmake dependency method, using auto or pkg-config will fallback to qmake. + +**Note** that using private headers in your project is a bad idea, do it at your own risks. \ No newline at end of file diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 99346ec8d..27820b94e 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -147,19 +147,20 @@ class GnuStepDependency(ConfigToolDependency): return version -def _qt_get_private_includes(mod_inc_dir, module, qt_version): +def _qt_get_private_includes(mod_inc_dir, module, mod_version): # usually Qt5 puts private headers in /QT_INSTALL_HEADERS/module/VERSION/module/private # except for at least QtWebkit and Enginio where the module version doesn't match Qt version # as an example with Qt 5.10.1 on linux you would get: # /usr/include/qt5/QtCore/5.10.1/QtCore/private/ # /usr/include/qt5/QtWidgets/5.10.1/QtWidgets/private/ - # /usr/include/qt5/Enginio/1.6.2/Enginio/private/ + # /usr/include/qt5/QtWebKit/5.212.0/QtWebKit/private/ # on Qt4 when available private folder is directly in module folder - if int(qt_version.split('.')[0]) < 5: + # like /usr/include/QtCore/private/ + if int(mod_version.split('.')[0]) < 5: return tuple() - private_dir = os.path.join(mod_inc_dir, qt_version) + private_dir = os.path.join(mod_inc_dir, mod_version) # fallback, let's try to find a directory with the latest version if not os.path.exists(private_dir): dirs = [filename for filename in os.listdir(mod_inc_dir) @@ -255,19 +256,19 @@ class QtBaseDependency(ExternalDependency): # qmake-based fallback if pkg-config fails. kwargs['required'] = False modules = OrderedDict() - # Until Qt's pkg-config files provide private headers path - # let's just fallback to qmake method - if self.private_headers: - self.is_found = False - return for module in mods: modules[module] = PkgConfigDependency(self.qtpkgname + module, self.env, kwargs, language=self.language) - for m in modules.values(): + for m_name, m in modules.items(): if not m.found(): self.is_found = False return self.compile_args += m.get_compile_args() + if self.private_headers: + qt_inc_dir = m.get_pkgconfig_variable('includedir', dict()) + mod_private_inc = _qt_get_private_includes(os.path.join(qt_inc_dir, 'Qt' + m_name), m_name, m.version) + for dir in mod_private_inc: + self.compile_args.append('-I' + dir) self.link_args += m.get_link_args() self.is_found = True self.version = m.version From 7cfe970cef7e913279b55f7b3261b53b7f2a50a1 Mon Sep 17 00:00:00 2001 From: Alexis Jeandet Date: Sun, 27 May 2018 21:20:42 +0200 Subject: [PATCH 8/8] [Qt module] private_headers kwarg documentation reformulation Signed-off-by: Alexis Jeandet --- docs/markdown/Qt5-module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md index 7b0bf2896..0185a64dc 100644 --- a/docs/markdown/Qt5-module.md +++ b/docs/markdown/Qt5-module.md @@ -42,7 +42,7 @@ See the Qt documentation for the [list of modules](http://doc.qt.io/qt-5/qtmodul ## private headers (since v0.47.0) -Some projects needs Qt's private headers to build, that's why a **private_headers** keyword argument has been added to [dependency](Reference-manual.md#dependency) method. -Setting this optionnal argument will add private include path of the given module to the compiler flags. +**private_headers** keyword argument has been added to [dependency](Reference-manual.md#dependency) method to allow Qt's modules private headers usage. +Setting this optional argument to true will add private include path of the given module to the compiler flags. **Note** that using private headers in your project is a bad idea, do it at your own risks. \ No newline at end of file