summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2019-01-18 12:29:32 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-03-05 11:44:13 +0000
commit6e3fba8f4ac75645480bb12216f46eeb413c2936 (patch)
tree52d0c6313b8b41ccafce8780b665b1019558f549
parent80691fb41939ca0bb90819a25c5f6ef06850a9e2 (diff)
Say hello to qp5_tool.py
qp5_tool.py is a developer helper tool modeled after the qt5_tool Perl script of Qt 5. It maintains a configuration file where per-directory values (build options, module subsets) can be given. Creating an up-to-date Build is then done by calling: python build_scripts/qp5_tool.py -c -p -b in each directory, Change-Id: Ifdcf414f7ff89512bb81a132c0bfd2e49275ed24 Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io> Reviewed-by: Cristian Maureira-Fredes <cristian.maureira-fredes@qt.io>
-rw-r--r--build_scripts/qp5_tool.py261
1 files changed, 261 insertions, 0 deletions
diff --git a/build_scripts/qp5_tool.py b/build_scripts/qp5_tool.py
new file mode 100644
index 00000000..aca8cf7f
--- /dev/null
+++ b/build_scripts/qp5_tool.py
@@ -0,0 +1,261 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## 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 Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## 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-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from __future__ import print_function
+
+from argparse import ArgumentParser, RawTextHelpFormatter
+import os
+import re
+import subprocess
+import sys
+import warnings
+
+desc = """
+Utility script for working with Qt for Python.
+
+Feel free to extend!
+
+qp5_tool.py can be configured by creating a configuration file
+in the format key=value:
+ "%CONFIGFILE%"
+
+It is possible to use repository-specific values
+by adding a key postfixed by a dash and the repository folder base name, eg:
+Modules-pyside-setup512=Core,Gui,Widgets,Network,Test
+
+Configuration keys:
+Modules Comma separated list of modules to be built
+ (for --module-subset=)
+BuildArguments Arguments to setup.py
+Python Python executable (Use python_d for debug builds on Windows)
+
+Arbitrary keys can be defined and referenced by $(name):
+
+MinimalModules=Core,Gui,Widgets,Network,Test
+Modules=$(MinimalModules),Multimedia
+Modules-pyside-setup-minimal=$(MinimalModules)
+"""
+
+def which(needle):
+ """Perform a path search"""
+ needles = [needle]
+ if is_windows:
+ for ext in ("exe", "bat", "cmd"):
+ needles.append("{}.{}".format(needle, ext))
+
+ for path in os.environ.get("PATH", "").split(os.pathsep):
+ for n in needles:
+ binary = os.path.join(path, n)
+ if os.path.isfile(binary):
+ return binary
+ return None
+
+def execute(args):
+ """Execute a command and print to log"""
+ log_string = '[{}] {}'.format(os.path.basename(os.getcwd()), ' '.join(args))
+ print(log_string)
+ exit_code = subprocess.call(args)
+ if exit_code != 0:
+ raise RuntimeError('FAIL({}): {}'.format(exit_code, log_string))
+
+def run_git(args):
+ """Run git in the current directory and its submodules"""
+ args.insert(0, git) # run in repo
+ execute(args) # run for submodules
+ module_args = [git, "submodule", "foreach"]
+ module_args.extend(args)
+ execute(module_args)
+
+def expand_reference(dict, value):
+ """Expand references to other keys in config files $(name) by value."""
+ pattern = re.compile(r"\$\([^)]+\)")
+ while True:
+ match = pattern.match(value)
+ if not match:
+ break
+ key = match.group(0)[2:-1]
+ value = value[:match.start(0)] + dict[key] + value[match.end(0):]
+ return value
+
+"""
+Config file handling, cache and read function
+"""
+
+config_dict = {}
+
+def read_config_file(fileName):
+ global config_dict
+ keyPattern = re.compile(r'^\s*([A-Za-z0-9\_\-]+)\s*=\s*(.*)$')
+ with open(config_file) as f:
+ while True:
+ line = f.readline().rstrip()
+ if not line:
+ break
+ match = keyPattern.match(line)
+ if match:
+ key = match.group(1)
+ value = match.group(2)
+ while value.endswith('\\'):
+ value = value.rstrip('\\')
+ value += f.readline().rstrip()
+ config_dict[key] = expand_reference(config_dict, value)
+
+def read_tool_config(key):
+ """
+ Read a value from the '$HOME/.qp5_tool' configuration file. When given
+ a key 'key' for the repository directory '/foo/qt-5', check for the
+ repo-specific value 'key-qt5' and then for the general 'key'.
+ """
+ if not config_dict:
+ read_config_file(config_file)
+ repo_value = config_dict.get(key + '-' + base_dir)
+ return repo_value if repo_value else config_dict.get(key)
+
+def read_config_build_arguments():
+ value = read_tool_config('BuildArguments')
+ if value:
+ return re.split(r'\s+', value)
+ return default_build_args;
+
+def read_config_modules_argument():
+ value = read_tool_config('Modules')
+ if value and value != '' and value != 'all':
+ return '--module-subset=' + value
+ return None
+
+def read_config_python_binary():
+ binary = read_tool_config('Python')
+ return binary if binary else 'python'
+
+def get_config_file():
+ home = os.getenv('HOME')
+ if is_windows:
+ # Set a HOME variable on Windows such that scp. etc.
+ # feel at home (locating .ssh).
+ if not home:
+ home = os.getenv('HOMEDRIVE') + os.getenv('HOMEPATH')
+ os.environ['HOME'] = home
+ user = os.getenv('USERNAME')
+ config_file = os.path.join(os.getenv('APPDATA'), config_file_name)
+ else:
+ user = os.getenv('USER')
+ config_dir = os.path.join(home, '.config')
+ if os.path.exists(config_dir):
+ config_file = os.path.join(config_dir, config_file_name)
+ else:
+ config_file = os.path.join(home, '.' + config_file_name)
+ return config_file
+
+def get_options(desc):
+ parser = ArgumentParser(description=desc, formatter_class=RawTextHelpFormatter)
+ parser.add_argument('--reset', '-r', action='store_true',
+ help='Git reset hard to upstream state')
+ parser.add_argument('--clean', '-c', action='store_true',
+ help='Git clean')
+ parser.add_argument('--pull', '-p', action='store_true',
+ help='Git pull')
+ parser.add_argument('--build', '-b', action='store_true',
+ help='Build (configure + build)')
+ parser.add_argument('--make', '-m', action='store_true', help='Make')
+ parser.add_argument('--Make', '-M', action='store_true',
+ help='cmake + Make (continue broken build)')
+ parser.add_argument('--version', '-v', action='version', version='%(prog)s 1.0')
+
+ return parser.parse_args()
+
+if __name__ == '__main__':
+
+ git = None
+ base_dir = None
+ default_build_args = ['--build-tests', '--skip-docs', '--quiet']
+ is_windows = sys.platform == 'win32'
+ config_file_name = 'qp5_tool.conf'
+ config_file = None
+ user = None
+ default_config_file = """
+ Modules=Core,Gui,Widgets,Network,Test,Qml,Quick,Multimedia,MultimediaWidgets
+ BuildArguments={}
+ # Python executable (python_d for debug builds)
+ Python=python
+ """
+
+ config_file = get_config_file()
+ desc = desc.replace('%CONFIGFILE%', config_file)
+ options = get_options(desc)
+
+ git = which('git')
+ if git is None:
+ warnings.warn('Unable to find git', RuntimeWarning)
+ sys.exit(-1)
+
+ if not os.path.exists(config_file):
+ print('Create initial config file ', config_file, " ..")
+ with open(config_file, 'w') as f:
+ f.write(default_config_file.format(' '.join(default_build_args)))
+
+ while not os.path.exists('.gitmodules'):
+ cwd = os.getcwd()
+ if cwd == '/' or (is_windows and len(cwd) < 4):
+ warnings.warn('Unable to find git root', RuntimeWarning)
+ sys.exit(-1)
+ os.chdir(os.path.dirname(cwd))
+
+ base_dir = os.path.basename(os.getcwd())
+
+ if options.clean:
+ run_git(['clean', '-dxf'])
+
+ if options.reset:
+ run_git(['reset', '--hard', '@{upstream}'])
+
+ if options.pull:
+ run_git(['pull', '--rebase'])
+
+ if options.build or options.make or options.Make:
+ arguments = [read_config_python_binary(), 'setup.py', 'install']
+ arguments.extend(read_config_build_arguments())
+ if options.make or options.Make:
+ arguments.extend(['--reuse-build', '--ignore-git'])
+ if not options.Make:
+ arguments.append('--skip-cmake')
+ modules = read_config_modules_argument()
+ if modules:
+ arguments.append(modules)
+ execute(arguments)
+ sys.exit(0)