Handle top level tests, examples and main qmake projects in pro2cmake

The script now detects whether the project file given is a:
- top level qmake project (qtdeclarative.pro)
- top level tests project (qtdeclarative/tests.pro)
- top level examples project (qtdeclarative/examples.pro)

This is done by finding the .qmake.conf file in parent directories of
the given project, and comparing the project's location to the
.qmake.conf location.

For the top level qmake project and the tests project, the script will
now use a predefined block of code that we usually had to copy paste and
change manually. Now only small bits will have to be adjusted manually
(project name and dependencies).

For the examples project, the content is surrounded by the required
build examples commands.

As a result, developers will have to worry less about knowing where to
copy paste from.

Change-Id: I4f751b371e74eeb86e070d58635c3d99b970ab18
Reviewed-by: Qt CMake Build Bot
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Alexandru Croitor 2019-09-09 10:52:27 +02:00
parent 1b1248d7cd
commit 3215f5457a

View File

@ -39,6 +39,7 @@ import os.path
import re
import io
import typing
import glob
from sympy.logic import (simplify_logic, And, Or, Not,)
import pyparsing as pp
@ -53,6 +54,8 @@ from shutil import copyfile
from special_case_helper import SpecialCaseHandler
cmake_version_string = "3.15.0"
def _parse_commandline():
parser = ArgumentParser(description='Generate CMakeLists.txt files from .'
'pro files.')
@ -96,6 +99,40 @@ def _parse_commandline():
return parser.parse_args()
def is_top_level_repo_project(project_file_path: str = '') -> bool:
qmake_conf_path = find_qmake_conf(project_file_path)
qmake_conf_dir_path = os.path.dirname(qmake_conf_path)
project_dir_path = os.path.dirname(project_file_path)
if qmake_conf_dir_path == project_dir_path:
return True
return False
def is_top_level_repo_tests_project(project_file_path: str = '') -> bool:
qmake_conf_path = find_qmake_conf(project_file_path)
qmake_conf_dir_path = os.path.dirname(qmake_conf_path)
project_dir_path = os.path.dirname(project_file_path)
project_dir_name = os.path.basename(project_dir_path)
maybe_same_level_dir_path = os.path.join(project_dir_path, "..")
normalized_maybe_same_level_dir_path = os.path.normpath(maybe_same_level_dir_path)
if qmake_conf_dir_path == normalized_maybe_same_level_dir_path and project_dir_name == 'tests':
return True
return False
def is_top_level_repo_examples_project(project_file_path: str = '') -> bool:
qmake_conf_path = find_qmake_conf(project_file_path)
qmake_conf_dir_path = os.path.dirname(qmake_conf_path)
project_dir_path = os.path.dirname(project_file_path)
project_dir_name = os.path.basename(project_dir_path)
maybe_same_level_dir_path = os.path.join(project_dir_path, "..")
normalized_maybe_same_level_dir_path = os.path.normpath(maybe_same_level_dir_path)
if qmake_conf_dir_path == normalized_maybe_same_level_dir_path \
and project_dir_name == 'examples':
return True
return False
def find_qmake_conf(project_file_path: str = '') -> typing.Optional[str]:
if not os.path.isabs(project_file_path):
print('Warning: could not find .qmake.conf file, given path is not an absolute path: {}'
@ -2274,17 +2311,125 @@ def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *,
footer = ')\n')
def handle_top_level_repo_project(scope: Scope, cm_fh: typing.IO[str]):
# qtdeclarative
project_file_name = os.path.splitext(os.path.basename(scope.file_absolute_path))[0]
# declarative
file_name_without_qt_prefix = project_file_name[2:]
# Qt::Declarative
qt_lib = map_qt_library(file_name_without_qt_prefix)
# Found a mapping, adjust name.
if qt_lib != file_name_without_qt_prefix:
# QtDeclarative
qt_lib = re.sub(r':', r'', qt_lib)
# Declarative
qt_lib_no_prefix = qt_lib[2:]
else:
qt_lib += "_FIXME"
qt_lib_no_prefix = qt_lib
content = """cmake_minimum_required(VERSION {})
project({}
VERSION 6.0.0
DESCRIPTION "Qt {} Libraries"
HOMEPAGE_URL "https://qt.io/"
LANGUAGES CXX C
)
find_package(Qt6 ${{PROJECT_VERSION}} CONFIG REQUIRED COMPONENTS BuildInternals Core SET_ME_TO_SOMETHING_USEFUL)
find_package(Qt6 ${{PROJECT_VERSION}} CONFIG OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL)
qt_build_repo()
""".format(cmake_version_string, qt_lib, qt_lib_no_prefix)
cm_fh.write('{}'.format(content))
def find_top_level_repo_project_file(project_file_path: str = '') -> typing.Optional[str]:
qmake_conf_path = find_qmake_conf(project_file_path)
qmake_dir = os.path.dirname(qmake_conf_path)
# Hope to a programming god that there's only one .pro file at the
# top level directory of repository.
glob_result = glob.glob(os.path.join(qmake_dir, '*.pro'))
if len(glob_result) > 0:
return glob_result[0]
return None
def handle_top_level_repo_tests_project(scope: Scope, cm_fh: typing.IO[str]):
top_level_project_path = find_top_level_repo_project_file(scope.file_absolute_path)
if top_level_project_path:
# qtdeclarative
file_name = os.path.splitext(os.path.basename(top_level_project_path))[0]
# declarative
file_name_without_qt = file_name[2:]
# Qt::Declarative
qt_lib = map_qt_library(file_name_without_qt)
# Found a mapping, adjust name.
if qt_lib != file_name_without_qt:
# QtDeclarative
qt_lib = re.sub(r':', r'', qt_lib) + "Tests"
else:
qt_lib += "Tests_FIXME"
else:
qt_lib = "Tests_FIXME"
content = """if(NOT TARGET Qt::Test)
cmake_minimum_required(VERSION {})
project({} VERSION 6.0.0 LANGUAGES C CXX)
find_package(Qt6 ${{PROJECT_VERSION}} REQUIRED COMPONENTS BuildInternals Core SET_ME_TO_SOMETHING_USEFUL)
find_package(Qt6 ${{PROJECT_VERSION}} OPTIONAL_COMPONENTS SET_ME_TO_SOMETHING_USEFUL)
qt_set_up_standalone_tests_build()
endif()
qt_build_tests()
""".format(cmake_version_string, qt_lib)
cm_fh.write('{}'.format(content))
def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *,
indent: int = 0, is_example: bool=False) -> None:
template = scope.TEMPLATE
if template == 'subdirs':
handle_subdir(scope, cm_fh, indent=indent, is_example=is_example)
temp_buffer = io.StringIO()
# Handle top level repo project in a special way.
if is_top_level_repo_project(scope.file_absolute_path):
handle_top_level_repo_project(scope, temp_buffer)
# Same for top-level tests.
elif is_top_level_repo_tests_project(scope.file_absolute_path):
handle_top_level_repo_tests_project(scope, temp_buffer)
elif template == 'subdirs':
handle_subdir(scope, temp_buffer, indent=indent, is_example=is_example)
elif template in ('app', 'lib'):
handle_app_or_lib(scope, cm_fh, indent=indent, is_example=is_example)
handle_app_or_lib(scope, temp_buffer, indent=indent, is_example=is_example)
else:
print(' XXXX: {}: Template type {} not yet supported.'
.format(scope.file, template))
buffer_value = temp_buffer.getvalue()
if is_top_level_repo_examples_project(scope.file_absolute_path):
# Wrap top level examples project with some commands which
# are necessary to build examples as part of the overall
# build.
buffer_value = """qt_examples_build_begin()
{}
qt_examples_build_end()
""".format(buffer_value)
cm_fh.write(buffer_value)
def generate_new_cmakelists(scope: Scope, *, is_example: bool=False) -> None:
print('Generating CMakeLists.gen.txt')