1############################################################################# 2## 3## Copyright (C) 2018 The Qt Company Ltd. 4## Contact: https://www.qt.io/licensing/ 5## 6## This file is part of Qt for Python. 7## 8## $QT_BEGIN_LICENSE:LGPL$ 9## Commercial License Usage 10## Licensees holding valid commercial Qt licenses may use this file in 11## accordance with the commercial license agreement provided with the 12## Software or, alternatively, in accordance with the terms contained in 13## a written agreement between you and The Qt Company. For licensing terms 14## and conditions see https://www.qt.io/terms-conditions. For further 15## information use the contact form at https://www.qt.io/contact-us. 16## 17## GNU Lesser General Public License Usage 18## Alternatively, this file may be used under the terms of the GNU Lesser 19## General Public License version 3 as published by the Free Software 20## Foundation and appearing in the file LICENSE.LGPL3 included in the 21## packaging of this file. Please review the following information to 22## ensure the GNU Lesser General Public License version 3 requirements 23## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24## 25## GNU General Public License Usage 26## Alternatively, this file may be used under the terms of the GNU 27## General Public License version 2.0 or (at your option) the GNU General 28## Public license version 3 or any later version approved by the KDE Free 29## Qt Foundation. The licenses are as published by the Free Software 30## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31## included in the packaging of this file. Please review the following 32## information to ensure the GNU General Public License requirements will 33## be met: https://www.gnu.org/licenses/gpl-2.0.html and 34## https://www.gnu.org/licenses/gpl-3.0.html. 35## 36## $QT_END_LICENSE$ 37## 38############################################################################# 39 40import os 41import sys 42import re 43import subprocess 44 45 46def _effective_qmake_command(qmake, qt_version): 47 """Check whether qmake path is a link to qtchooser and append the 48 desired Qt version in that case""" 49 result = [qmake] 50 # Looking whether qmake path is a link to qtchooser and whether the link 51 # exists 52 if os.path.islink(qmake) and os.path.lexists(qmake): 53 if not qt_version: 54 print('--qt must be specified when using qtchooser.') 55 sys.exit(-1) 56 # Set -qt=X here. 57 if "qtchooser" in os.readlink(qmake): 58 result.append("-qt={}".format(qt_version)) 59 return result 60 61 62class QtInfo(object): 63 class __QtInfo: # Python singleton 64 def __init__(self): 65 self._qmake_command = None 66 # Dict to cache qmake values. 67 self._query_dict = {} 68 # Dict to cache mkspecs variables. 69 self._mkspecs_dict = {} 70 71 def setup(self, qmake, qt_version): 72 self._qmake_command = _effective_qmake_command(qmake, qt_version) 73 74 def get_qmake_command(self): 75 qmake_command_string = self._qmake_command[0] 76 for entry in self._qmake_command[1:]: 77 qmake_command_string += " {}".format(entry) 78 return qmake_command_string 79 80 def get_version(self): 81 return self.get_property("QT_VERSION") 82 83 def get_bins_path(self): 84 return self.get_property("QT_INSTALL_BINS") 85 86 def get_libs_path(self): 87 return self.get_property("QT_INSTALL_LIBS") 88 89 def get_libs_execs_path(self): 90 return self.get_property("QT_INSTALL_LIBEXECS") 91 92 def get_plugins_path(self): 93 return self.get_property("QT_INSTALL_PLUGINS") 94 95 def get_prefix_path(self): 96 return self.get_property("QT_INSTALL_PREFIX") 97 98 def get_imports_path(self): 99 return self.get_property("QT_INSTALL_IMPORTS") 100 101 def get_translations_path(self): 102 return self.get_property("QT_INSTALL_TRANSLATIONS") 103 104 def get_headers_path(self): 105 return self.get_property("QT_INSTALL_HEADERS") 106 107 def get_docs_path(self): 108 return self.get_property("QT_INSTALL_DOCS") 109 110 def get_qml_path(self): 111 return self.get_property("QT_INSTALL_QML") 112 113 def get_macos_deployment_target(self): 114 """ Return value is a macOS version or None. """ 115 return self.get_property("QMAKE_MACOSX_DEPLOYMENT_TARGET") 116 117 def get_build_type(self): 118 """ 119 Return value is either debug, release, debug_release, or None. 120 """ 121 return self.get_property("BUILD_TYPE") 122 123 def get_src_dir(self): 124 """ Return path to Qt src dir or None.. """ 125 return self.get_property("QT_INSTALL_PREFIX/src") 126 127 def get_property(self, prop_name): 128 if not self._query_dict: 129 self._get_query_properties() 130 self._get_other_properties() 131 if prop_name not in self._query_dict: 132 return None 133 return self._query_dict[prop_name] 134 135 def get_mkspecs_variables(self): 136 return self._mkspecs_dict 137 138 def _get_qmake_output(self, args_list=[]): 139 assert(self._qmake_command) 140 cmd = self._qmake_command + args_list 141 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=False) 142 output = proc.communicate()[0] 143 proc.wait() 144 if proc.returncode != 0: 145 return "" 146 if sys.version_info >= (3,): 147 output = str(output, 'ascii').strip() 148 else: 149 output = output.strip() 150 return output 151 152 def _parse_query_properties(self, process_output): 153 props = {} 154 if not process_output: 155 return props 156 lines = [s.strip() for s in process_output.splitlines()] 157 for line in lines: 158 if line and ':' in line: 159 key, value = line.split(':', 1) 160 props[key] = value 161 return props 162 163 def _get_query_properties(self): 164 output = self._get_qmake_output(['-query']) 165 self._query_dict = self._parse_query_properties(output) 166 167 def _parse_qt_build_type(self): 168 key = 'QT_CONFIG' 169 if key not in self._mkspecs_dict: 170 return None 171 172 qt_config = self._mkspecs_dict[key] 173 if 'debug_and_release' in qt_config: 174 return 'debug_and_release' 175 176 split = qt_config.split(' ') 177 if 'release' in split and 'debug' in split: 178 return 'debug_and_release' 179 180 if 'release' in split: 181 return 'release' 182 183 if 'debug' in split: 184 return 'debug' 185 186 return None 187 188 def _get_other_properties(self): 189 # Get the src property separately, because it is not returned by 190 # qmake unless explicitly specified. 191 key = 'QT_INSTALL_PREFIX/src' 192 result = self._get_qmake_output(['-query', key]) 193 self._query_dict[key] = result 194 195 # Get mkspecs variables and cache them. 196 self._get_qmake_mkspecs_variables() 197 198 # Get macOS minimum deployment target. 199 key = 'QMAKE_MACOSX_DEPLOYMENT_TARGET' 200 if key in self._mkspecs_dict: 201 self._query_dict[key] = self._mkspecs_dict[key] 202 203 # Figure out how Qt was built: 204 # debug mode, release mode, or both. 205 build_type = self._parse_qt_build_type() 206 if build_type: 207 self._query_dict['BUILD_TYPE'] = build_type 208 209 def _get_qmake_mkspecs_variables(self): 210 # Create empty temporary qmake project file. 211 temp_file_name = 'qmake_fake_empty_project.txt' 212 open(temp_file_name, 'a').close() 213 214 # Query qmake for all of its mkspecs variables. 215 qmake_output = self._get_qmake_output(['-E', temp_file_name]) 216 lines = [s.strip() for s in qmake_output.splitlines()] 217 pattern = re.compile(r"^(.+?)=(.+?)$") 218 for line in lines: 219 found = pattern.search(line) 220 if found: 221 key = found.group(1).strip() 222 value = found.group(2).strip() 223 self._mkspecs_dict[key] = value 224 225 # We need to clean up after qmake, which always creates a 226 # .qmake.stash file after a -E invocation. 227 qmake_stash_file = os.path.join(os.getcwd(), ".qmake.stash") 228 if os.path.exists(qmake_stash_file): 229 os.remove(qmake_stash_file) 230 231 # Also clean up the temporary empty project file. 232 if os.path.exists(temp_file_name): 233 os.remove(temp_file_name) 234 235 version = property(get_version) 236 bins_dir = property(get_bins_path) 237 libs_dir = property(get_libs_path) 238 lib_execs_dir = property(get_libs_execs_path) 239 plugins_dir = property(get_plugins_path) 240 prefix_dir = property(get_prefix_path) 241 qmake_command = property(get_qmake_command) 242 imports_dir = property(get_imports_path) 243 translations_dir = property(get_translations_path) 244 headers_dir = property(get_headers_path) 245 docs_dir = property(get_docs_path) 246 qml_dir = property(get_qml_path) 247 macos_min_deployment_target = property(get_macos_deployment_target) 248 build_type = property(get_build_type) 249 src_dir = property(get_src_dir) 250 251 _instance = None # singleton helpers 252 253 def __new__(cls): # __new__ always a classmethod 254 if not QtInfo._instance: 255 QtInfo._instance = QtInfo.__QtInfo() 256 return QtInfo._instance 257 258 def __getattr__(self, name): 259 return getattr(self._instance, name) 260 261 def __setattr__(self, name): 262 return setattr(self._instance, name) 263