From 361ae8d22b762e627a0bad6a6ce8dd7a0def2dd4 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 18 Dec 2016 13:38:39 +0530 Subject: [PATCH 1/4] qt: Fix detection of tools on Windows When you pass an absolute path to shutil.which, it will not implicitly append any extensions. This is problematic on Windows, so we need to account for that. This fixes detection of Qt tools on Windows which are searched with the full path to the Qt bindir. --- mesonbuild/dependencies.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 73062014c..183835d38 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -373,6 +373,8 @@ class WxDependency(Dependency): return self.is_found class ExternalProgram(): + windows_exts = ('exe', 'com', 'bat') + def __init__(self, name, fullpath=None, silent=False, search_dir=None): self.name = name if fullpath is not None: @@ -410,11 +412,10 @@ class ExternalProgram(): pass return False - @staticmethod - def _is_executable(path): + def _is_executable(self, path): suffix = os.path.splitext(path)[-1].lower()[1:] if mesonlib.is_windows(): - if suffix == 'exe' or suffix == 'com' or suffix == 'bat': + if suffix in self.windows_exts: return True elif os.access(path, os.X_OK): return True @@ -424,10 +425,15 @@ class ExternalProgram(): if search_dir is None: return False trial = os.path.join(search_dir, name) - if not os.path.exists(trial): + if os.path.exists(trial): + if self._is_executable(trial): + return [trial] + else: + for ext in self.windows_exts: + trial_ext = '{}.{}'.format(trial, ext) + if os.path.exists(trial_ext): + return [trial_ext] return False - if self._is_executable(trial): - return [trial] # Now getting desperate. Maybe it is a script file that is a) not chmodded # executable or b) we are on windows so they can't be directly executed. return self._shebang_to_cmd(trial) @@ -441,6 +447,11 @@ class ExternalProgram(): if fullpath or not mesonlib.is_windows(): # On UNIX-like platforms, the standard PATH search is enough return [fullpath] + # On Windows, if name is an absolute path, we need the extension too + for ext in self.windows_exts: + fullpath = '{}.{}'.format(name, ext) + if os.path.exists(fullpath): + return [fullpath] # On Windows, interpreted scripts must have an extension otherwise they # cannot be found by a standard PATH search. So we do a custom search # where we manually search for a script with a shebang in PATH. From ef2c2eeed1f30a4eace485a6a5e401bb60d6fcd9 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 18 Dec 2016 13:40:50 +0530 Subject: [PATCH 2/4] QtDependency: Only use -fPIC on Linux It is enabled by default on OS X and on it doesn't make sense on Windows. --- mesonbuild/dependencies.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 183835d38..9ef1649a2 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -1029,8 +1029,9 @@ class QtBaseDependency(Dependency): # penalty when using self-built Qt or on platforms # where -fPIC is not required. If this is an issue # for you, patches are welcome. - # Fix this to be more portable, especially to MSVC. - return ['-fPIC'] + if mesonlib.is_linux(): + return ['-fPIC'] + return [] class Qt5Dependency(QtBaseDependency): def __init__(self, env, kwargs): From ade1f695a6105f2e39299e707b5f2e1120e37f0d Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 18 Dec 2016 13:41:56 +0530 Subject: [PATCH 3/4] Fix detection of pkg-config on all platforms a) Don't search for pkg-config if we're only cross-compiling b) Don't unconditionally error out while cross-compiling if the specified pkg-config is not found and the dependency is optional c) Use the pkg-config binary that was found in check_pkgconfig for the actual testing d) Use shutil.which on the found pkg-config only if it finds it. Sometimes shutil.which fails to find it, for instance on Windows with absolute paths. --- mesonbuild/dependencies.py | 72 ++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 9ef1649a2..20556e227 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -92,7 +92,7 @@ class InternalDependency(Dependency): return self.version class PkgConfigDependency(Dependency): - pkgconfig_found = None + pkgbin = None def __init__(self, name, environment, kwargs): Dependency.__init__(self, 'pkgconfig') @@ -109,36 +109,40 @@ class PkgConfigDependency(Dependency): else: want_cross = environment.is_cross_build() self.name = name - if PkgConfigDependency.pkgconfig_found is None: - self.check_pkgconfig() + + # When finding dependencies for cross-compiling, we don't care about + # the 'native' pkg-config + if want_cross: + if 'pkgconfig' not in env.cross_info.config['binaries']: + if self.required: + raise DependencyException('Pkg-config binary missing from cross file') + else: + pkgbin = environment.cross_info.config['binaries']['pkgconfig'] + # Only search for the native pkg-config the first time and + # store the result in the class definition + elif PkgConfigDependency.pkgbin is None: + PkgConfigDependency.pkgbin = self.check_pkgconfig() self.is_found = False - if not PkgConfigDependency.pkgconfig_found: + if not self.pkgbin: if self.required: raise DependencyException('Pkg-config not found.') return - if environment.is_cross_build() and want_cross: - if "pkgconfig" not in environment.cross_info.config["binaries"]: - raise DependencyException('Pkg-config binary missing from cross file.') - pkgbin = environment.cross_info.config["binaries"]['pkgconfig'] + if want_cross: self.type_string = 'Cross' else: - evar = 'PKG_CONFIG' - if evar in os.environ: - pkgbin = os.environ[evar].strip() - else: - pkgbin = 'pkg-config' self.type_string = 'Native' - mlog.debug('Determining dependency %s with pkg-config executable %s.' % (name, pkgbin)) - self.pkgbin = pkgbin + mlog.debug('Determining dependency {!r} with pkg-config executable ' + '{!r}'.format(name, self.pkgbin)) ret, self.modversion = self._call_pkgbin(['--modversion', name]) if ret != 0: if self.required: - raise DependencyException('%s dependency %s not found.' % (self.type_string, name)) + raise DependencyException('{} dependency {!r} not found' + ''.format(self.type_string, name)) self.modversion = 'none' return - found_msg = ['%s dependency' % self.type_string, mlog.bold(name), 'found:'] + found_msg = [self.type_string + ' dependency', mlog.bold(name), 'found:'] self.version_reqs = kwargs.get('version', None) if self.version_reqs is None: self.is_found = True @@ -236,24 +240,30 @@ class PkgConfigDependency(Dependency): return self.libs def check_pkgconfig(self): + evar = 'PKG_CONFIG' + if evar in os.environ: + pkgbin = os.environ[evar].strip() + else: + pkgbin = 'pkg-config' try: - evar = 'PKG_CONFIG' - if evar in os.environ: - pkgbin = os.environ[evar].strip() - else: - pkgbin = 'pkg-config' p, out = Popen_safe([pkgbin, '--version'])[0:2] - if p.returncode == 0: - if not self.silent: - mlog.log('Found pkg-config:', mlog.bold(shutil.which(pkgbin)), - '(%s)' % out.strip()) - PkgConfigDependency.pkgconfig_found = True - return + if p.returncode != 0: + # Set to False instead of None to signify that we've already + # searched for it and not found it + pkgbin = False except (FileNotFoundError, PermissionError): - pass - PkgConfigDependency.pkgconfig_found = False + pkgbin = False + if pkgbin and not os.path.isabs(pkgbin) and shutil.which(pkgbin): + # Sometimes shutil.which fails where Popen succeeds, so + # only find the abs path if it can be found by shutil.which + pkgbin = shutil.which(pkgbin) if not self.silent: - mlog.log('Found Pkg-config:', mlog.red('NO')) + if pkgbin: + mlog.log('Found pkg-config:', mlog.bold(pkgbin), + '(%s)' % out.strip()) + else: + mlog.log('Found Pkg-config:', mlog.red('NO')) + return pkgbin def found(self): return self.is_found From eaafca6f4af11290331cbc4788688407c0fb654f Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Sun, 18 Dec 2016 14:26:18 +0530 Subject: [PATCH 4/4] Qt: Allow passing a name arg to preprocess() This sets a unique name for the CustomTarget and the output cpp file. Closes #959 --- mesonbuild/modules/qt4.py | 15 +++++++++------ mesonbuild/modules/qt5.py | 17 ++++++++++------- test cases/frameworks/4 qt/meson.build | 3 +++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index 63dfef8bf..2d89792d4 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -107,10 +107,10 @@ class Qt4Module(): moc_sources = kwargs.pop('moc_sources', []) if not isinstance(moc_sources, list): moc_sources = [moc_sources] - srctmp = kwargs.pop('sources', []) - if not isinstance(srctmp, list): - srctmp = [srctmp] - sources = args[1:] + srctmp + sources = kwargs.pop('sources', []) + if not isinstance(sources, list): + sources = [sources] + sources += args[1:] self._detect_tools(state.environment) err_msg = "{0} sources specified and couldn't find {1}, " \ "please check your qt4 installation" @@ -122,8 +122,11 @@ class Qt4Module(): qrc_deps = [] for i in rcc_files: qrc_deps += self.parse_qrc(state, i) - basename = os.path.split(rcc_files[0])[1] - name = 'qt4-' + basename.replace('.', '_') + if len(args) > 0: + name = args[0] + else: + basename = os.path.split(rcc_files[0])[1] + name = 'qt4-' + basename.replace('.', '_') rcc_kwargs = {'input' : rcc_files, 'output' : name + '.cpp', 'command' : [self.rcc, '-o', '@OUTPUT@', '@INPUT@'], diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 56c626964..da1ac834a 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -113,10 +113,10 @@ class Qt5Module(): moc_sources = kwargs.pop('moc_sources', []) if not isinstance(moc_sources, list): moc_sources = [moc_sources] - srctmp = kwargs.pop('sources', []) - if not isinstance(srctmp, list): - srctmp = [srctmp] - sources = args[1:] + srctmp + sources = kwargs.pop('sources', []) + if not isinstance(sources, list): + sources = [sources] + sources += args[1:] self._detect_tools(state.environment) err_msg = "{0} sources specified and couldn't find {1}, " \ "please check your qt5 installation" @@ -128,13 +128,16 @@ class Qt5Module(): qrc_deps = [] for i in rcc_files: qrc_deps += self.parse_qrc(state, i) - basename = os.path.split(rcc_files[0])[1] + if len(args) > 0: + name = args[0] + else: + basename = os.path.split(rcc_files[0])[1] + name = 'qt5-' + basename.replace('.', '_') rcc_kwargs = {'input' : rcc_files, - 'output' : basename + '.cpp', + 'output' : name + '.cpp', 'command' : [self.rcc, '-o', '@OUTPUT@', '@INPUT@'], 'depend_files' : qrc_deps, } - name = 'qt5-' + basename.replace('.', '_') res_target = build.CustomTarget(name, state.subdir, rcc_kwargs) sources.append(res_target) if len(ui_files) > 0: diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index 013f14df1..4523babf8 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -30,6 +30,9 @@ foreach qt : ['qt4', 'qt5'] qresources : ['stuff.qrc', 'stuff2.qrc'], # Resource file for rcc compiler. ) + # Test that setting a unique name with a positional argument works + qtmodule.preprocess(qt + 'teststuff', qresources : ['stuff.qrc']) + qexe = executable(qt + 'app', sources : ['main.cpp', 'mainWindow.cpp', # Sources that don't need preprocessing. prep],