summaryrefslogtreecommitdiffstats
path: root/util/cmake/run_pro2cmake.py
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2020-01-31 11:43:22 +0100
committerSimon Hausmann <simon.hausmann@qt.io>2020-02-03 15:01:53 +0000
commit4e7af2061e8c323b2a21f0549643a2cfab191664 (patch)
tree54c6046915aee4a91eb6178183c022b8e547d244 /util/cmake/run_pro2cmake.py
parent6251963ecd26bfc480b2871e26b6df4d7ab88cee (diff)
parent3f386095adc6c280008c7e811e88f0215f1d862f (diff)
Merge remote-tracking branch 'origin/wip/cmake' into dev
This pulls the CMake port, which not only adds CMake files but also modifies existing code. A brief summary of "seemingly unrelated" changes: * configure.json was re-formatted to not use multi-line strings. That is an extension of the Qt JSON parser but not JSON compliant, which is needed for the configure.json-to-cmake conversion script (python). * Some moc inclusions were added due to CMake's slightly different way of handling moc. With the changes the files build with qmake and cmake. * Since CMake just grep's for the Q_OBJECT macro to determine whether to call moc (instead of doing pre-processing like qmake), the existing use of "Q_OBJECT" in our documentation was changed to \Q_OBJECT, which cmake doesn't see and which is now a qdoc macro. * QTestLib's qFindTestData was extended to also search in the source directory known at build time. What this change also brings is a new way of building modules in Coin by using YAML configuration files that describe the steps of building and testing in Coin specific terms. The platform configuration files in qt5 are instructed to use the old Coin built-in way of testing ("UseLegacyInstructions" feature) but for any configurations that do not have this, these yaml files in the coin/ sub-directory are used and shared across repositories. Change-Id: I1d832c3400e8d6945ad787024ba60e7440225c08
Diffstat (limited to 'util/cmake/run_pro2cmake.py')
-rwxr-xr-xutil/cmake/run_pro2cmake.py246
1 files changed, 246 insertions, 0 deletions
diff --git a/util/cmake/run_pro2cmake.py b/util/cmake/run_pro2cmake.py
new file mode 100755
index 0000000000..4a12c57b83
--- /dev/null
+++ b/util/cmake/run_pro2cmake.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+#############################################################################
+##
+## Copyright (C) 2018 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the plugins 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 glob
+import os
+import subprocess
+import concurrent.futures
+import sys
+import typing
+import argparse
+from argparse import ArgumentParser
+
+
+def parse_command_line() -> argparse.Namespace:
+ parser = ArgumentParser(
+ description="Run pro2cmake on all .pro files recursively in given path. "
+ "You can pass additional arguments to the pro2cmake calls by appending "
+ "-- --foo --bar"
+ )
+ parser.add_argument(
+ "--only-existing",
+ dest="only_existing",
+ action="store_true",
+ help="Run pro2cmake only on .pro files that already have a CMakeLists.txt.",
+ )
+ parser.add_argument(
+ "--only-missing",
+ dest="only_missing",
+ action="store_true",
+ help="Run pro2cmake only on .pro files that do not have a CMakeLists.txt.",
+ )
+ parser.add_argument(
+ "--only-qtbase-main-modules",
+ dest="only_qtbase_main_modules",
+ action="store_true",
+ help="Run pro2cmake only on the main modules in qtbase.",
+ )
+ parser.add_argument(
+ "--skip-subdirs-projects",
+ dest="skip_subdirs_projects",
+ action="store_true",
+ help="Don't run pro2cmake on TEMPLATE=subdirs projects.",
+ )
+ parser.add_argument(
+ "--is-example",
+ dest="is_example",
+ action="store_true",
+ help="Run pro2cmake with --is-example flag.",
+ )
+ parser.add_argument(
+ "--count", dest="count", help="How many projects should be converted.", type=int
+ )
+ parser.add_argument(
+ "--offset",
+ dest="offset",
+ help="From the list of found projects, from which project should conversion begin.",
+ type=int,
+ )
+ parser.add_argument(
+ "path", metavar="<path>", type=str, help="The path where to look for .pro files."
+ )
+
+ args, unknown = parser.parse_known_args()
+
+ # Error out when the unknown arguments do not start with a "--",
+ # which implies passing through arguments to pro2cmake.
+ if len(unknown) > 0 and unknown[0] != "--":
+ parser.error("unrecognized arguments: {}".format(" ".join(unknown)))
+ else:
+ args.pro2cmake_args = unknown[1:]
+
+ return args
+
+
+def find_all_pro_files(base_path: str, args: argparse.Namespace):
+ def sorter(pro_file: str) -> str:
+ """ Sorter that tries to prioritize main pro files in a directory. """
+ pro_file_without_suffix = pro_file.rsplit("/", 1)[-1][:-4]
+ dir_name = os.path.dirname(pro_file)
+ if dir_name == ".":
+ dir_name = os.path.basename(os.getcwd())
+ if dir_name.endswith(pro_file_without_suffix):
+ return dir_name
+ return dir_name + "/__" + pro_file
+
+ all_files = []
+ previous_dir_name: typing.Optional[str] = None
+
+ print("Finding .pro files.")
+ glob_result = glob.glob(os.path.join(base_path, "**/*.pro"), recursive=True)
+
+ def cmake_lists_exists_filter(path):
+ path_dir_name = os.path.dirname(path)
+ if os.path.exists(os.path.join(path_dir_name, "CMakeLists.txt")):
+ return True
+ return False
+
+ def cmake_lists_missing_filter(path):
+ return not cmake_lists_exists_filter(path)
+
+ def qtbase_main_modules_filter(path):
+ main_modules = [
+ "corelib",
+ "network",
+ "gui",
+ "widgets",
+ "testlib",
+ "printsupport",
+ "opengl",
+ "sql",
+ "dbus",
+ "concurrent",
+ "xml",
+ ]
+ path_suffixes = [f"src/{m}/{m}.pro" for m in main_modules]
+
+ for path_suffix in path_suffixes:
+ if path.endswith(path_suffix):
+ return True
+ return False
+
+ filter_result = glob_result
+ filter_func = None
+ if args.only_existing:
+ filter_func = cmake_lists_exists_filter
+ elif args.only_missing:
+ filter_func = cmake_lists_missing_filter
+ elif args.only_qtbase_main_modules:
+ filter_func = qtbase_main_modules_filter
+
+ if filter_func:
+ print("Filtering.")
+ filter_result = [p for p in filter_result if filter_func(p)]
+
+ for pro_file in sorted(filter_result, key=sorter):
+ dir_name = os.path.dirname(pro_file)
+ if dir_name == previous_dir_name:
+ print("Skipping:", pro_file)
+ else:
+ all_files.append(pro_file)
+ previous_dir_name = dir_name
+ return all_files
+
+
+def run(all_files: typing.List[str], pro2cmake: str, args: argparse.Namespace) -> typing.List[str]:
+ failed_files = []
+ files_count = len(all_files)
+ workers = os.cpu_count() or 1
+
+ if args.only_qtbase_main_modules:
+ # qtbase main modules take longer than usual to process.
+ workers = 2
+
+ with concurrent.futures.ThreadPoolExecutor(max_workers=workers, initargs=(10,)) as pool:
+ print("Firing up thread pool executor.")
+
+ def _process_a_file(data: typing.Tuple[str, int, int]) -> typing.Tuple[int, str, str]:
+ filename, index, total = data
+ pro2cmake_args = []
+ if sys.platform == "win32":
+ pro2cmake_args.append(sys.executable)
+ pro2cmake_args.append(pro2cmake)
+ if args.is_example:
+ pro2cmake_args.append("--is-example")
+ if args.skip_subdirs_projects:
+ pro2cmake_args.append("--skip-subdirs-project")
+ pro2cmake_args.append(os.path.basename(filename))
+
+ if args.pro2cmake_args:
+ pro2cmake_args += args.pro2cmake_args
+
+ result = subprocess.run(
+ pro2cmake_args,
+ cwd=os.path.dirname(filename),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ stdout = f"Converted[{index}/{total}]: {filename}\n"
+ return result.returncode, filename, stdout + result.stdout.decode()
+
+ for return_code, filename, stdout in pool.map(
+ _process_a_file,
+ zip(all_files, range(1, files_count + 1), (files_count for _ in all_files)),
+ ):
+ if return_code:
+ failed_files.append(filename)
+ print(stdout)
+
+ return failed_files
+
+
+def main() -> None:
+ args = parse_command_line()
+
+ script_path = os.path.dirname(os.path.abspath(__file__))
+ pro2cmake = os.path.join(script_path, "pro2cmake.py")
+ base_path = args.path
+
+ all_files = find_all_pro_files(base_path, args)
+ if args.offset:
+ all_files = all_files[args.offset :]
+ if args.count:
+ all_files = all_files[: args.count]
+ files_count = len(all_files)
+
+ failed_files = run(all_files, pro2cmake, args)
+ if len(all_files) == 0:
+ print("No files found.")
+
+ if failed_files:
+ print(
+ f"The following files were not successfully "
+ f"converted ({len(failed_files)} of {files_count}):"
+ )
+ for f in failed_files:
+ print(f' "{f}"')
+
+
+if __name__ == "__main__":
+ main()