| #!/usr/bin/env python |
| |
| ############################################################################# |
| ## |
| ## Copyright (C) 2015 The Qt Company Ltd. |
| ## Contact: https://www.qt.io/licensing/ |
| ## |
| ## This file is part of the QtWebEngine module of the Qt Toolkit. |
| ## |
| ## $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ## Commercial License Usage |
| ## Licensees holding valid commercial Qt licenses may use this file in |
| ## accordance with the commercial license agreement provided with the |
| ## Software or, alternatively, in accordance with the terms contained in |
| ## a written agreement between you and The Qt Company. For licensing terms |
| ## and conditions see https://www.qt.io/terms-conditions. For further |
| ## information use the contact form at https://www.qt.io/contact-us. |
| ## |
| ## GNU General Public License Usage |
| ## Alternatively, this file may be used under the terms of the GNU |
| ## General Public License version 3 as published by the Free Software |
| ## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ## included in the packaging of this file. Please review the following |
| ## information to ensure the GNU General Public License requirements will |
| ## be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ## |
| ## $QT_END_LICENSE$ |
| ## |
| ############################################################################# |
| |
| import argparse |
| import os |
| import re |
| import subprocess |
| import shutil |
| import sys |
| |
| |
| class Api: |
| QUICK = "webengine" |
| WIDGET = "webenginewidgets" |
| |
| |
| class Mode: |
| RELEASE = "release" |
| DEBUG = "debug" |
| |
| |
| def is_windows(): |
| return os.name == "nt" |
| |
| |
| class ArgManager(object): |
| def __init__(self): |
| try: |
| self.__args = self.__parse() |
| except: |
| raise |
| |
| self.build_dir = self.__args.build_dir |
| self.src_dir = self.__args.src_dir |
| self.example_filter = re.compile("%s-%s-%s" % (self.__api_filter(), self.__name_filter(), self.__mode_filter())) |
| self.list_examples = self.__args.list_examples |
| self.force = self.__args.force |
| self.verbose = self.__args.verbose |
| self.out_dir = self.__args.out_dir |
| |
| def __parse(self): |
| ap = argparse.ArgumentParser(description="Deploy QtWebEngine example binaries on Windows.") |
| ap.add_argument("--release", dest="release", action="store_true", default=False, |
| help="Deploy release binaries only") |
| ap.add_argument("--debug", dest="debug", action="store_true", default=False, |
| help="Deploy debug binaries only") |
| ap.add_argument("--quick", dest="quick", action="store_true", default=False, |
| help="Deploy quick examples only") |
| ap.add_argument("--widget", dest="widget", action="store_true", default=False, |
| help="Deploy widget examples only") |
| ap.add_argument("-e", "--examples", dest="examples", nargs="+", action="store", default=".*", |
| help="Select example to deploy") |
| ap.add_argument("-l", "--list-examples", dest="list_examples", action="store_true", default=False, |
| help="List available examples") |
| ap.add_argument("-f", "--force", dest="force", action="store_true", default=False, |
| help="Force to overwrite existing files") |
| ap.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=False, |
| help="Print windeployqt output") |
| ap.add_argument("--src-dir", dest="src_dir", action="store", type=self.__validate_src_dir, default=None, |
| help="Specify path of Qt sources. It is used for finding QML files of the example" |
| "and scanning for QML imports") |
| ap.add_argument("--build-dir", dest="build_dir", action="store", type=self.__validate_build_dir, default=None, |
| help="Specify path of the Qt binaries. It is used for finding qmake.exe, windeployqt.exe and" |
| "binaries of the examples. It is not necessary to set if qmake.exe is set in the path") |
| ap.add_argument("-o", "--out-dir", dest="out_dir", action="store", default=os.getcwd(), |
| help="Specify path for the deployed examples. If it is not set" |
| "current working directory is used") |
| return ap.parse_args() |
| |
| def __validate_src_dir(self, src_dir): |
| if not os.path.exists(src_dir): |
| raise OSError("The specified Qt source directory does not exist: %s" % src_dir) |
| |
| qtwebengine_dir = src_dir |
| # Accept Qt top level source directory too |
| if os.path.exists(os.path.join(src_dir, "qtwebengine")): |
| qtwebengine_dir = os.path.join(src_dir, "qtwebengine") |
| |
| examples_dir = os.path.join(qtwebengine_dir, "examples") |
| must_have_paths = [ |
| os.path.join(examples_dir, "examples.pro"), |
| os.path.join(examples_dir, Api.QUICK), |
| os.path.join(examples_dir, Api.WIDGET), |
| ] |
| |
| # Check whether src_dir is the proper QtWebEngine source directory |
| for p in must_have_paths: |
| if not os.path.exists(p): |
| raise OSError("The specified Qt source directory is invalid: %s" % src_dir) |
| |
| return src_dir |
| |
| def __validate_build_dir(self, build_dir): |
| if not os.path.exists(build_dir): |
| raise OSError("The specified Qt build directory does not exist: %s" % build_dir) |
| |
| # Accept QtWebEngine build directory too |
| if os.path.basename(build_dir) == "qtwebengine": |
| build_dir = os.path.abspath(os.path.join(build_dir, "..")) |
| |
| # Attempt to support custom build directories |
| if os.path.exists(os.path.join(build_dir, "bin", "qmake.exe")): |
| return build_dir |
| |
| # Check existence of qtbase/bin/qmake.exe |
| qtbase_dir = os.path.join(build_dir, "qtbase") |
| if not os.path.exists(os.path.join(qtbase_dir, "bin", "qmake.exe")): |
| raise OSError("Program 'qmake.exe' cannot be found in the specified Qt build directory: %s" % build_dir) |
| |
| return qtbase_dir |
| |
| def __mode_filter(self): |
| if self.__args.release and not self.__args.debug: |
| return Mode.RELEASE |
| if not self.__args.release and self.__args.debug: |
| return Mode.DEBUG |
| return ".*" |
| |
| def __api_filter(self): |
| if self.__args.quick and not self.__args.widget: |
| return Api.QUICK |
| if not self.__args.quick and self.__args.widget: |
| return Api.WIDGET |
| return ".*" |
| |
| def __name_filter(self): |
| alt_list = [] |
| |
| examples = self.__args.examples |
| if not isinstance(examples, list): |
| examples = [examples] |
| |
| for e in examples: |
| if "," in e: |
| alt_list.extend(e.split(",")) |
| else: |
| alt_list.append(e) |
| |
| return "|".join(alt_list) |
| |
| |
| class QtHelper(object): |
| def __init__(self, build_path=None, src_path=None): |
| self.__build_path = build_path if build_path is not None else "" |
| self.__src_path = src_path if src_path is not None else "" |
| self.__query = {} |
| self.__angle = None |
| |
| self.__qmake = "qmake.exe" |
| self.__windeployqt = "windeployqt.exe" |
| if build_path: |
| self.__qmake = os.path.join(self.__build_path, "bin", self.__qmake) |
| self.__windeployqt = os.path.join(self.__build_path, "bin", self.__windeployqt) |
| |
| try: |
| program = self.__qmake |
| subprocess.check_output([program, "-v"]) |
| program = self.__windeployqt |
| subprocess.check_output([program, "-h"]) |
| except OSError as e: |
| raise OSError("Program '%s' cannot be executed\n%s" % (program, e)) |
| except: |
| raise |
| |
| self.__query = self.get_query() |
| self.__build_path = self.get_build_path() |
| self.__src_path = self.get_src_path() |
| self.__angle = self.has_angle() |
| |
| def get_query(self): |
| if self.__query: |
| return self.__query |
| |
| qmake_output = subprocess.check_output([self.__qmake, "-query"]).split("\r\n") |
| query = {} |
| for line in qmake_output: |
| entry = line.split(":", 1) |
| if len(entry) != 2: |
| continue |
| query[entry[0]] = entry[1] |
| return query |
| |
| def get_build_path(self): |
| return os.path.abspath(os.path.join(self.__query["QT_INSTALL_PREFIX"], "..")) |
| |
| def get_src_path(self): |
| if self.__src_path: |
| return self.__src_path |
| |
| if "QT_INSTALL_PREFIX/src" in self.__query: |
| return os.path.abspath(os.path.join(self.__query["QT_INSTALL_PREFIX/src"], "..")) |
| |
| return self.__build_path |
| |
| def get_windeployqt(self): |
| return self.__windeployqt |
| |
| def has_angle(self): |
| if self.__angle: |
| return self.__angle |
| |
| qconfig_pri_path = os.path.abspath(os.path.join(self.__query["QT_HOST_PREFIX"], "mkspecs", "qconfig.pri")) |
| print(qconfig_pri_path) |
| if not os.path.exists(qconfig_pri_path): |
| sys.stderr.write("Configuration file qconfig.pri cannot be found. Fallback to desktop GL mode.\n") |
| return False |
| |
| qt_config = [] |
| |
| qconfig_pri = open(qconfig_pri_path, "r") |
| for line in qconfig_pri: |
| if line.startswith("QT_CONFIG +="): |
| qt_config = re.match("^QT_CONFIG \+= (.+)$", line).group(1).split(" ") |
| qconfig_pri.close() |
| |
| return "angle" in qt_config |
| |
| def collect_examples(self): |
| examples = [] |
| |
| for api in [Api.QUICK, Api.WIDGET]: |
| examples_root_dir_path = os.path.join(self.__build_path, "qtwebengine", "examples", api) |
| if not os.path.exists(examples_root_dir_path): |
| continue |
| |
| for example in os.listdir(examples_root_dir_path): |
| example_dir_path = os.path.join(examples_root_dir_path, example) |
| if not os.path.exists(example_dir_path): |
| continue |
| |
| example_src_path = os.path.join(self.__src_path, "qtwebengine", "examples", api, example) |
| |
| for mode in [Mode.RELEASE, Mode.DEBUG]: |
| example_exe_path = os.path.join(example_dir_path, mode, example + ".exe") |
| if not os.path.exists(example_exe_path): |
| continue |
| examples.append(Example(example, api, mode, self.__angle, example_exe_path, example_src_path)) |
| |
| return examples |
| |
| |
| class Example(str): |
| def __new__(cls, example, api, mode, angle, exe_path, src_path): |
| obj = str.__new__(cls, "%s-%s-%s" % (api, example, mode)) |
| return obj |
| |
| def __init__(self, example, api, mode, angle, exe_path, src_path): |
| super(Example, self).__init__("%s-%s-%s" % (api, example, mode)) |
| self.__name = example |
| self.__api = api |
| self.__mode = mode |
| self.__angle = angle |
| self.__exe_path = exe_path |
| self.__src_path = src_path |
| |
| def get_deploy_params(self, mode, force): |
| deploy_params = [] |
| deploy_params.append("--%s" % mode) |
| deploy_params.append("--compiler-runtime") |
| |
| if self.__angle: |
| deploy_params.append("--angle") |
| |
| if force: |
| deploy_params.append("--force") |
| |
| if self.__api is Api.QUICK: |
| deploy_params.append("--qmldir") |
| deploy_params.append(self.__src_path) |
| |
| if self.__mode is Mode.DEBUG: |
| deploy_params.append("--pdb") |
| |
| return deploy_params |
| |
| def name(self): |
| return self.__name |
| |
| def deploy(self, qt, dest_path, force=False, verbose=False): |
| src_path = os.path.dirname(self.__exe_path) |
| for f in os.listdir(src_path): |
| shutil.copy(os.path.join(src_path, f), dest_path) |
| |
| deploy_command = [] |
| deploy_command.append(qt.get_windeployqt()) |
| # Debug executables also need the release libraries |
| deploy_command.extend(self.get_deploy_params(Mode.RELEASE, force)) |
| deploy_command.append(dest_path) |
| |
| if verbose: |
| print("%s" % " ".join(deploy_command)) |
| |
| out = None if verbose else open(os.devnull, "w") |
| exit_code = subprocess.call(deploy_command, stdout=out) |
| |
| if self.__mode is Mode.DEBUG and not exit_code: |
| param_release = "--%s" % Mode.RELEASE |
| param_debug = "--%s" % Mode.DEBUG |
| deploy_command = map(lambda item: param_debug if item == param_release else item, deploy_command) |
| |
| if verbose: |
| print("%s" % " ".join(deploy_command)) |
| |
| exit_code = subprocess.call(deploy_command, stdout=out) |
| |
| if out is not None: |
| out.close() |
| |
| return exit_code |
| |
| |
| def main(): |
| try: |
| args = ArgManager() |
| |
| if not is_windows(): |
| raise OSError("This script works on Windows only\n") |
| |
| qt = QtHelper(args.build_dir, args.src_dir) |
| except Exception as e: |
| sys.stderr.write(str(e)) |
| exit(1) |
| |
| example_list = filter((lambda e: args.example_filter.match(e)), qt.collect_examples()) |
| if not example_list: |
| print("There is no example that fulfills the requirements") |
| return |
| |
| for example in example_list: |
| if args.list_examples: |
| print(example.name()) |
| continue |
| |
| print("Deploying %s ..." % example.name()) |
| dest_path = os.path.abspath(os.path.join(args.out_dir, example.name())) |
| if not os.path.exists(dest_path): |
| os.makedirs(dest_path) |
| elif os.listdir(dest_path) and not args.force: |
| sys.stderr.write("Destination directory is not empty: %s\n" % dest_path) |
| sys.stderr.write("Skip deploying %s\n" % example.name()) |
| continue |
| |
| exit_code = example.deploy(qt, dest_path, args.force, args.verbose) |
| if exit_code: |
| sys.stderr.write("Deploy of example '%s' has failed: %s\n" % (example.name(), exit_code)) |
| exit(exit_code) |
| |
| print("Example '%s' has been successfully deployed at %s" % (example.name(), dest_path)) |
| |
| return |
| |
| if __name__ == "__main__": |
| main() |