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 functools 41import os 42import sys 43import fnmatch 44 45from ..config import config 46from ..options import OPTION 47from ..utils import copydir, copyfile, makefile 48from ..utils import regenerate_qt_resources, filter_match 49from ..utils import download_and_extract_7z 50 51 52def prepare_packages_win32(self, vars): 53 # For now, debug symbols will not be shipped into the package. 54 copy_pdbs = False 55 pdbs = [] 56 if (self.debug or self.build_type == 'RelWithDebInfo') and copy_pdbs: 57 pdbs = ['*.pdb'] 58 59 # <install>/lib/site-packages/{st_package_name}/* -> 60 # <setup>/{st_package_name} 61 # This copies the module .pyd files and various .py files 62 # (__init__, config, git version, etc.) 63 copydir( 64 "{site_packages_dir}/{st_package_name}", 65 "{st_build_dir}/{st_package_name}", 66 vars=vars) 67 68 if config.is_internal_shiboken_module_build(): 69 # <build>/shiboken2/doc/html/* -> 70 # <setup>/{st_package_name}/docs/shiboken2 71 copydir( 72 "{build_dir}/shiboken2/doc/html", 73 "{st_build_dir}/{st_package_name}/docs/shiboken2", 74 force=False, vars=vars) 75 76 # <install>/bin/*.dll -> {st_package_name}/ 77 copydir( 78 "{install_dir}/bin/", 79 "{st_build_dir}/{st_package_name}", 80 filter=["shiboken*.dll"], 81 recursive=False, vars=vars) 82 83 # <install>/lib/*.lib -> {st_package_name}/ 84 copydir( 85 "{install_dir}/lib/", 86 "{st_build_dir}/{st_package_name}", 87 filter=["shiboken*.lib"], 88 recursive=False, vars=vars) 89 90 # @TODO: Fix this .pdb file not to overwrite release 91 # {shibokengenerator}.pdb file. 92 # Task-number: PYSIDE-615 93 copydir( 94 "{build_dir}/shiboken2/shibokenmodule", 95 "{st_build_dir}/{st_package_name}", 96 filter=pdbs, 97 recursive=False, vars=vars) 98 99 # pdb files for libshiboken and libpyside 100 copydir( 101 "{build_dir}/shiboken2/libshiboken", 102 "{st_build_dir}/{st_package_name}", 103 filter=pdbs, 104 recursive=False, vars=vars) 105 106 if config.is_internal_shiboken_generator_build(): 107 # <install>/bin/*.dll -> {st_package_name}/ 108 copydir( 109 "{install_dir}/bin/", 110 "{st_build_dir}/{st_package_name}", 111 filter=["shiboken*.exe"], 112 recursive=False, vars=vars) 113 114 # Used to create scripts directory. 115 makefile( 116 "{st_build_dir}/{st_package_name}/scripts/shiboken_tool.py", 117 vars=vars) 118 119 # For setting up setuptools entry points. 120 copyfile( 121 "{install_dir}/bin/shiboken_tool.py", 122 "{st_build_dir}/{st_package_name}/scripts/shiboken_tool.py", 123 force=False, vars=vars) 124 125 # @TODO: Fix this .pdb file not to overwrite release 126 # {shibokenmodule}.pdb file. 127 # Task-number: PYSIDE-615 128 copydir( 129 "{build_dir}/shiboken2/generator", 130 "{st_build_dir}/{st_package_name}", 131 filter=pdbs, 132 recursive=False, vars=vars) 133 134 if config.is_internal_shiboken_generator_build() or config.is_internal_pyside_build(): 135 # <install>/include/* -> <setup>/{st_package_name}/include 136 copydir( 137 "{install_dir}/include/{cmake_package_name}", 138 "{st_build_dir}/{st_package_name}/include", 139 vars=vars) 140 141 if config.is_internal_pyside_build(): 142 # <build>/pyside2/{st_package_name}/*.pdb -> 143 # <setup>/{st_package_name} 144 copydir( 145 "{build_dir}/pyside2/{st_package_name}", 146 "{st_build_dir}/{st_package_name}", 147 filter=pdbs, 148 recursive=False, vars=vars) 149 150 makefile( 151 "{st_build_dir}/{st_package_name}/scripts/__init__.py", 152 vars=vars) 153 154 # For setting up setuptools entry points 155 copyfile( 156 "{install_dir}/bin/pyside_tool.py", 157 "{st_build_dir}/{st_package_name}/scripts/pyside_tool.py", 158 force=False, vars=vars) 159 160 # <install>/bin/*.exe,*.dll -> {st_package_name}/ 161 copydir( 162 "{install_dir}/bin/", 163 "{st_build_dir}/{st_package_name}", 164 filter=["pyside*.exe", "pyside*.dll", "uic.exe", "rcc.exe", "designer.exe"], 165 recursive=False, vars=vars) 166 167 # <install>/lib/*.lib -> {st_package_name}/ 168 copydir( 169 "{install_dir}/lib/", 170 "{st_build_dir}/{st_package_name}", 171 filter=["pyside*.lib"], 172 recursive=False, vars=vars) 173 174 # <install>/share/{st_package_name}/typesystems/* -> 175 # <setup>/{st_package_name}/typesystems 176 copydir( 177 "{install_dir}/share/{st_package_name}/typesystems", 178 "{st_build_dir}/{st_package_name}/typesystems", 179 vars=vars) 180 181 # <install>/share/{st_package_name}/glue/* -> 182 # <setup>/{st_package_name}/glue 183 copydir( 184 "{install_dir}/share/{st_package_name}/glue", 185 "{st_build_dir}/{st_package_name}/glue", 186 vars=vars) 187 188 # <source>/pyside2/{st_package_name}/support/* -> 189 # <setup>/{st_package_name}/support/* 190 copydir( 191 "{build_dir}/pyside2/{st_package_name}/support", 192 "{st_build_dir}/{st_package_name}/support", 193 vars=vars) 194 195 # <source>/pyside2/{st_package_name}/*.pyi -> 196 # <setup>/{st_package_name}/*.pyi 197 copydir( 198 "{build_dir}/pyside2/{st_package_name}", 199 "{st_build_dir}/{st_package_name}", 200 filter=["*.pyi", "py.typed"], 201 vars=vars) 202 203 copydir( 204 "{build_dir}/pyside2/libpyside", 205 "{st_build_dir}/{st_package_name}", 206 filter=pdbs, 207 recursive=False, vars=vars) 208 209 if not OPTION["NOEXAMPLES"]: 210 def pycache_dir_filter(dir_name, parent_full_path, dir_full_path): 211 if fnmatch.fnmatch(dir_name, "__pycache__"): 212 return False 213 return True 214 # examples/* -> <setup>/{st_package_name}/examples 215 copydir(os.path.join(self.script_dir, "examples"), 216 "{st_build_dir}/{st_package_name}/examples", 217 force=False, vars=vars, dir_filter_function=pycache_dir_filter) 218 # Re-generate examples Qt resource files for Python 3 219 # compatibility 220 if sys.version_info[0] == 3: 221 examples_path = "{st_build_dir}/{st_package_name}/examples".format( 222 **vars) 223 pyside_rcc_path = "{install_dir}/bin/rcc.exe".format( 224 **vars) 225 pyside_rcc_options = ['-g', 'python'] 226 regenerate_qt_resources(examples_path, pyside_rcc_path, pyside_rcc_options) 227 228 if vars['ssl_libs_dir']: 229 # <ssl_libs>/* -> <setup>/{st_package_name}/openssl 230 copydir("{ssl_libs_dir}", "{st_build_dir}/{st_package_name}/openssl", 231 filter=[ 232 "libeay32.dll", 233 "ssleay32.dll"], 234 force=False, vars=vars) 235 236 if config.is_internal_shiboken_module_build(): 237 # The C++ std library dlls need to be packaged with the 238 # shiboken module, because libshiboken uses C++ code. 239 copy_msvc_redist_files(vars, "{build_dir}/msvc_redist".format(**vars)) 240 241 if config.is_internal_pyside_build() or config.is_internal_shiboken_generator_build(): 242 copy_qt_artifacts(self, copy_pdbs, vars) 243 copy_msvc_redist_files(vars, "{build_dir}/msvc_redist".format(**vars)) 244 245 246def copy_msvc_redist_files(vars, redist_target_path): 247 # MSVC redistributable file list. 248 msvc_redist = [ 249 "concrt140.dll", 250 "msvcp140.dll", 251 "ucrtbase.dll", 252 "vcamp140.dll", 253 "vccorlib140.dll", 254 "vcomp140.dll", 255 "vcruntime140.dll", 256 "vcruntime140_1.dll", 257 "msvcp140_1.dll", 258 "msvcp140_2.dll", 259 "msvcp140_codecvt_ids.dll" 260 ] 261 262 # Make a directory where the files should be extracted. 263 if not os.path.exists(redist_target_path): 264 os.makedirs(redist_target_path) 265 266 # Extract Qt dependency dlls when building on Qt CI. 267 in_coin = os.environ.get('COIN_LAUNCH_PARAMETERS', None) 268 if in_coin is not None: 269 redist_url = "http://download.qt.io/development_releases/prebuilt/vcredist/" 270 zip_file = "pyside_qt_deps_64_2019.7z" 271 if "{target_arch}".format(**vars) == "32": 272 zip_file = "pyside_qt_deps_32_2019.7z" 273 download_and_extract_7z(redist_url + zip_file, redist_target_path) 274 else: 275 print("Qt dependency DLLs (MSVC redist) will not be downloaded and extracted.") 276 277 copydir(redist_target_path, 278 "{st_build_dir}/{st_package_name}", 279 filter=msvc_redist, recursive=False, vars=vars) 280 281 282def copy_qt_artifacts(self, copy_pdbs, vars): 283 built_modules = self.get_built_pyside_config(vars)['built_modules'] 284 285 constrain_modules = None 286 copy_plugins = True 287 copy_qml = True 288 copy_translations = True 289 copy_qt_conf = True 290 copy_qt_permanent_artifacts = True 291 copy_msvc_redist = False 292 copy_clang = False 293 294 if config.is_internal_shiboken_generator_build(): 295 constrain_modules = ["Core", "Network", "Xml", "XmlPatterns"] 296 copy_plugins = False 297 copy_qml = False 298 copy_translations = False 299 copy_qt_conf = False 300 copy_qt_permanent_artifacts = False 301 copy_msvc_redist = True 302 copy_clang = True 303 304 # <qt>/bin/*.dll and Qt *.exe -> <setup>/{st_package_name} 305 qt_artifacts_permanent = [ 306 "opengl*.dll", 307 "d3d*.dll", 308 "designer.exe", 309 "linguist.exe", 310 "lrelease.exe", 311 "lupdate.exe", 312 "lconvert.exe", 313 "qtdiag.exe" 314 ] 315 316 # Choose which EGL library variants to copy. 317 qt_artifacts_egl = [ 318 "libEGL{}.dll", 319 "libGLESv2{}.dll" 320 ] 321 if self.qtinfo.build_type != 'debug_and_release': 322 egl_suffix = '*' 323 elif self.debug: 324 egl_suffix = 'd' 325 else: 326 egl_suffix = '' 327 qt_artifacts_egl = [a.format(egl_suffix) for a in qt_artifacts_egl] 328 329 artifacts = [] 330 if copy_qt_permanent_artifacts: 331 artifacts += qt_artifacts_permanent 332 artifacts += qt_artifacts_egl 333 334 if copy_msvc_redist: 335 # The target path has to be qt_bin_dir at the moment, 336 # because the extracted archive also contains the opengl32sw 337 # and the d3dcompiler dlls, which are copied not by this 338 # function, but by the copydir below. 339 copy_msvc_redist_files(vars, "{qt_bin_dir}".format(**vars)) 340 341 if artifacts: 342 copydir("{qt_bin_dir}", 343 "{st_build_dir}/{st_package_name}", 344 filter=artifacts, recursive=False, vars=vars) 345 346 # <qt>/bin/*.dll and Qt *.pdbs -> <setup>/{st_package_name} part two 347 # File filter to copy only debug or only release files. 348 if constrain_modules: 349 qt_dll_patterns = ["Qt5" + x + "{}.dll" for x in constrain_modules] 350 if copy_pdbs: 351 qt_dll_patterns += ["Qt5" + x + "{}.pdb" for x in constrain_modules] 352 else: 353 qt_dll_patterns = ["Qt5*{}.dll", "lib*{}.dll"] 354 if copy_pdbs: 355 qt_dll_patterns += ["Qt5*{}.pdb", "lib*{}.pdb"] 356 357 def qt_build_config_filter(patterns, file_name, file_full_path): 358 release = [a.format('') for a in patterns] 359 debug = [a.format('d') for a in patterns] 360 361 # If qt is not a debug_and_release build, that means there 362 # is only one set of shared libraries, so we can just copy 363 # them. 364 if self.qtinfo.build_type != 'debug_and_release': 365 if filter_match(file_name, release): 366 return True 367 return False 368 369 # In debug_and_release case, choosing which files to copy 370 # is more difficult. We want to copy only the files that 371 # match the PySide2 build type. So if PySide2 is built in 372 # debug mode, we want to copy only Qt debug libraries 373 # (ending with "d.dll"). Or vice versa. The problem is that 374 # some libraries have "d" as the last character of the 375 # actual library name (for example Qt5Gamepad.dll and 376 # Qt5Gamepadd.dll). So we can't just match a pattern ending 377 # in "d". Instead we check if there exists a file with the 378 # same name plus an additional "d" at the end, and using 379 # that information we can judge if the currently processed 380 # file is a debug or release file. 381 382 # e.g. ["Qt5Cored", ".dll"] 383 file_split = os.path.splitext(file_name) 384 file_base_name = file_split[0] 385 file_ext = file_split[1] 386 # e.g. "/home/work/qt/qtbase/bin" 387 file_path_dir_name = os.path.dirname(file_full_path) 388 # e.g. "Qt5Coredd" 389 maybe_debug_name = "{}d".format(file_base_name) 390 if self.debug: 391 filter = debug 392 393 def predicate(path): 394 return not os.path.exists(path) 395 else: 396 filter = release 397 398 def predicate(path): 399 return os.path.exists(path) 400 # e.g. "/home/work/qt/qtbase/bin/Qt5Coredd.dll" 401 other_config_path = os.path.join(file_path_dir_name, maybe_debug_name + file_ext) 402 403 if (filter_match(file_name, filter) and predicate(other_config_path)): 404 return True 405 return False 406 407 qt_dll_filter = functools.partial(qt_build_config_filter, 408 qt_dll_patterns) 409 copydir("{qt_bin_dir}", 410 "{st_build_dir}/{st_package_name}", 411 file_filter_function=qt_dll_filter, 412 recursive=False, vars=vars) 413 414 if copy_plugins: 415 # <qt>/plugins/* -> <setup>/{st_package_name}/plugins 416 plugin_dll_patterns = ["*{}.dll"] 417 pdb_pattern = "*{}.pdb" 418 if copy_pdbs: 419 plugin_dll_patterns += [pdb_pattern] 420 plugin_dll_filter = functools.partial(qt_build_config_filter, plugin_dll_patterns) 421 copydir("{qt_plugins_dir}", "{st_build_dir}/{st_package_name}/plugins", 422 file_filter_function=plugin_dll_filter, 423 vars=vars) 424 425 if copy_translations: 426 # <qt>/translations/* -> <setup>/{st_package_name}/translations 427 copydir("{qt_translations_dir}", 428 "{st_build_dir}/{st_package_name}/translations", 429 filter=["*.qm", "*.pak"], 430 force=False, 431 vars=vars) 432 433 if copy_qml: 434 # <qt>/qml/* -> <setup>/{st_package_name}/qml 435 qml_dll_patterns = ["*{}.dll"] 436 qml_ignore_patterns = qml_dll_patterns + [pdb_pattern] 437 qml_ignore = [a.format('') for a in qml_ignore_patterns] 438 439 # Copy all files that are not dlls and pdbs (.qml, qmldir). 440 copydir("{qt_qml_dir}", "{st_build_dir}/{st_package_name}/qml", 441 ignore=qml_ignore, 442 force=False, 443 recursive=True, 444 vars=vars) 445 446 if copy_pdbs: 447 qml_dll_patterns += [pdb_pattern] 448 qml_dll_filter = functools.partial(qt_build_config_filter, qml_dll_patterns) 449 450 # Copy all dlls (and possibly pdbs). 451 copydir("{qt_qml_dir}", "{st_build_dir}/{st_package_name}/qml", 452 file_filter_function=qml_dll_filter, 453 force=False, 454 recursive=True, 455 vars=vars) 456 457 if self.is_webengine_built(built_modules): 458 copydir("{qt_prefix_dir}/resources", 459 "{st_build_dir}/{st_package_name}/resources", 460 filter=None, 461 recursive=False, 462 vars=vars) 463 464 filter = 'QtWebEngineProcess{}.exe'.format( 465 'd' if self.debug else '') 466 copydir("{qt_bin_dir}", 467 "{st_build_dir}/{st_package_name}", 468 filter=[filter], 469 recursive=False, vars=vars) 470 471 if copy_qt_conf: 472 # Copy the qt.conf file to prefix dir. 473 copyfile("{build_dir}/pyside2/{st_package_name}/qt.conf", 474 "{st_build_dir}/{st_package_name}", 475 vars=vars) 476 477 if copy_clang: 478 self.prepare_standalone_clang(is_win=True) 479