summaryrefslogtreecommitdiffstats
path: root/util/cmake/pro_conversion_rate.py
diff options
context:
space:
mode:
Diffstat (limited to 'util/cmake/pro_conversion_rate.py')
-rwxr-xr-xutil/cmake/pro_conversion_rate.py218
1 files changed, 218 insertions, 0 deletions
diff --git a/util/cmake/pro_conversion_rate.py b/util/cmake/pro_conversion_rate.py
new file mode 100755
index 0000000000..740e834ca5
--- /dev/null
+++ b/util/cmake/pro_conversion_rate.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python3
+#############################################################################
+##
+## Copyright (C) 2019 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$
+##
+#############################################################################
+
+from __future__ import annotations
+
+"""
+This utility script shows statistics about
+converted .pro -> CMakeLists.txt files.
+
+To execute: python3 pro_conversion_rate.py <src dir>
+where <src dir> can be any qt source directory. For better statistics,
+specify a module root source dir (like ./qtbase or ./qtsvg).
+
+"""
+
+from argparse import ArgumentParser
+
+import os
+import typing
+from timeit import default_timer
+
+
+def _parse_commandline():
+ parser = ArgumentParser(description='Find pro files for which there are no CMakeLists.txt.')
+ parser.add_argument('source_directory', metavar='<src dir>', type=str,
+ help='The source directory')
+
+ return parser.parse_args()
+
+
+class Blacklist:
+ """ Class to check if a certain dir_name / dir_path is blacklisted """
+
+ def __init__(self, names: typing.List[str], path_parts: typing.List[str]):
+ self.names = names
+ self.path_parts = path_parts
+
+ # The lookup algorithm
+ self.lookup = self.is_blacklisted_part
+ self.tree = None
+
+ try:
+ # If package is available, use Aho-Corasick algorithm,
+ from ahocorapy.keywordtree import KeywordTree
+ self.tree = KeywordTree(case_insensitive=True)
+
+ for p in self.path_parts:
+ self.tree.add(p)
+ self.tree.finalize()
+
+ self.lookup = self.is_blacklisted_part_aho
+ except ImportError:
+ pass
+
+ def is_blacklisted(self, dir_name: str, dir_path: str) -> bool:
+ # First check if exact dir name is blacklisted.
+ if dir_name in self.names:
+ return True
+
+ # Check if a path part is blacklisted (e.g. util/cmake)
+ return self.lookup(dir_path)
+
+ def is_blacklisted_part(self, dir_path: str) -> bool:
+ if any(part in dir_path for part in self.path_parts):
+ return True
+ return False
+
+ def is_blacklisted_part_aho(self, dir_path: str) -> bool:
+ return self.tree.search(dir_path) is not None
+
+
+def recursive_scan(path: str, extension: str, result_paths: typing.List[str], blacklist: Blacklist):
+ """ Find files ending with a certain extension, filtering out blacklisted entries """
+ try:
+ for entry in os.scandir(path):
+ entry: os.DirEntry = entry
+
+ if entry.is_file() and entry.path.endswith(extension):
+ result_paths.append(entry.path)
+ elif entry.is_dir():
+ if blacklist.is_blacklisted(entry.name, entry.path):
+ continue
+ recursive_scan(entry.path, extension, result_paths, blacklist)
+ except Exception as e:
+ print(e)
+
+
+def check_for_cmake_project(pro_path: str) -> bool:
+ pro_dir_name = os.path.dirname(pro_path)
+ cmake_project_path = os.path.join(pro_dir_name, "CMakeLists.txt")
+ return os.path.exists(cmake_project_path)
+
+
+def compute_stats(src_path: str, pros_with_missing_project: typing.List[str],
+ total_pros: int, existing_pros: int, missing_pros: int) -> dict:
+ stats = {}
+ stats['total projects'] = {'label': 'Total pro files found',
+ 'value': total_pros}
+ stats['existing projects'] = {'label': 'Existing CMakeLists.txt files found',
+ 'value': existing_pros}
+ stats['missing projects'] = {'label': 'Missing CMakeLists.txt files found',
+ 'value': missing_pros}
+ stats['missing examples'] = {'label': 'Missing examples', 'value': 0}
+ stats['missing tests'] = {'label': 'Missing tests', 'value': 0}
+ stats['missing src'] = {'label': 'Missing src/**/**', 'value': 0}
+ stats['missing plugins'] = {'label': 'Missing plugins', 'value': 0}
+
+ for p in pros_with_missing_project:
+ rel_path = os.path.relpath(p, src_path)
+ if rel_path.startswith("examples"):
+ stats['missing examples']['value'] += 1
+ elif rel_path.startswith("tests"):
+ stats['missing tests']['value'] += 1
+ elif rel_path.startswith(os.path.join("src", "plugins")):
+ stats['missing plugins']['value'] += 1
+ elif rel_path.startswith("src"):
+ stats['missing src']['value'] += 1
+
+ for stat in stats:
+ if stats[stat]['value'] > 0:
+ stats[stat]['percentage'] = round(stats[stat]['value'] * 100 / total_pros, 2)
+ return stats
+
+
+def print_stats(src_path: str, pros_with_missing_project: typing.List[str], stats: dict,
+ scan_time: float, script_time: float):
+
+ if stats['total projects']['value'] == 0:
+ print("No .pro files found. Did you specify a correct source path?")
+ return
+
+ if stats['total projects']['value'] == stats['existing projects']['value']:
+ print("All projects were converted.")
+ else:
+ print("Missing CMakeLists.txt files for the following projects: \n")
+
+ for p in pros_with_missing_project:
+ rel_path = os.path.relpath(p, src_path)
+ print(rel_path)
+
+ print("\nStatistics: \n")
+
+ for stat in stats:
+ if stats[stat]['value'] > 0:
+ print("{:<40}: {} ({}%)".format(stats[stat]['label'],
+ stats[stat]['value'],
+ stats[stat]['percentage']))
+
+ print("\n{:<40}: {:.10f} seconds".format("Scan time", scan_time))
+ print("{:<40}: {:.10f} seconds".format("Total script time", script_time))
+
+
+def main():
+ args = _parse_commandline()
+ src_path = os.path.abspath(args.source_directory)
+ pro_paths = []
+
+ extension = ".pro"
+
+ blacklist_names = ["config.tests", "doc", "3rdparty", "angle"]
+ blacklist_path_parts = [
+ os.path.join("util", "cmake")
+ ]
+
+ script_start_time = default_timer()
+ blacklist = Blacklist(blacklist_names, blacklist_path_parts)
+
+ scan_time_start = default_timer()
+ recursive_scan(src_path, extension, pro_paths, blacklist)
+ scan_time_end = default_timer()
+ scan_time = scan_time_end - scan_time_start
+
+ total_pros = len(pro_paths)
+
+ pros_with_missing_project = []
+ for pro_path in pro_paths:
+ if not check_for_cmake_project(pro_path):
+ pros_with_missing_project.append(pro_path)
+
+ missing_pros = len(pros_with_missing_project)
+ existing_pros = total_pros - missing_pros
+
+ stats = compute_stats(src_path, pros_with_missing_project, total_pros, existing_pros,
+ missing_pros)
+ script_end_time = default_timer()
+ script_time = script_end_time - script_start_time
+
+ print_stats(src_path, pros_with_missing_project, stats, scan_time, script_time)
+
+
+if __name__ == '__main__':
+ main()