pro2cmake: Introduce qmake2cmake convenience scripts

Add qmake2cmake[.bat] wrapper scripts that can be used to convert user
projects to CMake.  For now, user projects are internally handled as Qt
examples with one difference: the generation of example-specific
installation code is suppressed.

Fixes: QTBUG-98655
Change-Id: I1a57f6d12efe0bdf383592ab33682a611692db80
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
Joerg Bornemann 2022-03-04 15:49:02 +01:00
parent 928828b549
commit bac56fd4d0
3 changed files with 111 additions and 53 deletions

View File

@ -82,7 +82,6 @@ from helper import (
LibraryMapping, LibraryMapping,
) )
cmake_version_string = "3.16" cmake_version_string = "3.16"
cmake_api_version = 3 cmake_api_version = 3
@ -136,7 +135,13 @@ def _parse_commandline():
"--is-example", "--is-example",
action="store_true", action="store_true",
dest="is_example", dest="is_example",
help="Treat the input .pro file as an example.", help="Treat the input .pro file as a Qt example.",
)
parser.add_argument(
"--is-user-project",
action="store_true",
dest="is_user_project",
help="Treat the input .pro file as a user project.",
) )
parser.add_argument( parser.add_argument(
"-s", "-s",
@ -1826,7 +1831,12 @@ def replace_path_constants(path: str, scope: Scope) -> str:
def handle_subdir( def handle_subdir(
scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False scope: Scope,
cm_fh: IO[str],
*,
indent: int = 0,
is_example: bool = False,
is_user_project: bool = False,
) -> None: ) -> None:
# Global nested dictionary that will contain sub_dir assignments and their conditions. # Global nested dictionary that will contain sub_dir assignments and their conditions.
@ -1891,7 +1901,13 @@ def handle_subdir(
) )
do_include(subdir_scope) do_include(subdir_scope)
cmakeify_scope(subdir_scope, cm_fh, indent=indent, is_example=is_example) cmakeify_scope(
subdir_scope,
cm_fh,
indent=indent,
is_example=is_example,
is_user_project=is_user_project,
)
else: else:
print(f" XXXX: SUBDIR {sd} in {scope}: Not found.") print(f" XXXX: SUBDIR {sd} in {scope}: Not found.")
@ -3890,19 +3906,26 @@ def find_qml_resource(resources: List[QtResource]):
def write_example( def write_example(
cm_fh: IO[str], scope: Scope, gui: bool = False, *, indent: int = 0, is_plugin: bool = False cm_fh: IO[str],
scope: Scope,
gui: bool = False,
*,
indent: int = 0,
is_plugin: bool = False,
is_user_project: bool = False,
) -> str: ) -> str:
binary_name = scope.TARGET binary_name = scope.TARGET
assert binary_name assert binary_name
config = scope.get("CONFIG") config = scope.get("CONFIG")
is_qml_plugin = ("qml" in scope.get("QT")) or "qmltypes" in config is_qml_plugin = ("qml" in scope.get("QT")) or "qmltypes" in config
example_install_dir = scope.expandString("target.path") if not is_user_project:
if not example_install_dir: example_install_dir = scope.expandString("target.path")
example_install_dir = "${INSTALL_EXAMPLESDIR}" if not example_install_dir:
example_install_dir = example_install_dir.replace( example_install_dir = "${INSTALL_EXAMPLESDIR}"
"$$[QT_INSTALL_EXAMPLES]", "${INSTALL_EXAMPLESDIR}" example_install_dir = example_install_dir.replace(
) "$$[QT_INSTALL_EXAMPLES]", "${INSTALL_EXAMPLESDIR}"
)
project_version = scope.get_string("VERSION", "1.0") project_version = scope.get_string("VERSION", "1.0")
cm_fh.write( cm_fh.write(
@ -3914,12 +3937,13 @@ def write_example(
if scope.get_files("FORMS"): if scope.get_files("FORMS"):
cm_fh.write("set(CMAKE_AUTOUIC ON)\n") cm_fh.write("set(CMAKE_AUTOUIC ON)\n")
cm_fh.write("\n") cm_fh.write("\n")
cm_fh.write( if not is_user_project:
"if(NOT DEFINED INSTALL_EXAMPLESDIR)\n" cm_fh.write(
' set(INSTALL_EXAMPLESDIR "examples")\n' "if(NOT DEFINED INSTALL_EXAMPLESDIR)\n"
"endif()\n\n" ' set(INSTALL_EXAMPLESDIR "examples")\n'
f'set(INSTALL_EXAMPLEDIR "{example_install_dir}")\n\n' "endif()\n\n"
) f'set(INSTALL_EXAMPLEDIR "{example_install_dir}")\n\n'
)
recursive_evaluate_scope(scope) recursive_evaluate_scope(scope)
@ -4108,13 +4132,14 @@ def write_example(
handling_first_scope = False handling_first_scope = False
cm_fh.write( if not is_user_project:
f"\ninstall(TARGETS {binary_name}\n" cm_fh.write(
f' RUNTIME DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n' f"\ninstall(TARGETS {binary_name}\n"
f' BUNDLE DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n' f' RUNTIME DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n'
f' LIBRARY DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n' f' BUNDLE DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n'
f")\n" f' LIBRARY DESTINATION "${{INSTALL_EXAMPLEDIR}}"\n'
) f")\n"
)
return binary_name return binary_name
@ -4505,7 +4530,12 @@ def write_qml_plugin_epilogue(
def handle_app_or_lib( def handle_app_or_lib(
scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False scope: Scope,
cm_fh: IO[str],
*,
indent: int = 0,
is_example: bool = False,
is_user_project=False,
) -> None: ) -> None:
assert scope.TEMPLATE in ("app", "lib") assert scope.TEMPLATE in ("app", "lib")
@ -4527,7 +4557,9 @@ def handle_app_or_lib(
assert not is_example assert not is_example
target = write_3rdparty_library(cm_fh, scope, indent=indent) target = write_3rdparty_library(cm_fh, scope, indent=indent)
elif is_example: elif is_example:
target = write_example(cm_fh, scope, gui, indent=indent, is_plugin=is_plugin) target = write_example(
cm_fh, scope, gui, indent=indent, is_plugin=is_plugin, is_user_project=is_user_project
)
elif is_qt_plugin: elif is_qt_plugin:
assert not is_example assert not is_example
target = write_plugin(cm_fh, scope, indent=indent) target = write_plugin(cm_fh, scope, indent=indent)
@ -4806,40 +4838,53 @@ endif()
def cmakeify_scope( def cmakeify_scope(
scope: Scope, cm_fh: IO[str], *, indent: int = 0, is_example: bool = False scope: Scope,
cm_fh: IO[str],
*,
indent: int = 0,
is_example: bool = False,
is_user_project: bool = False,
) -> None: ) -> None:
template = scope.TEMPLATE template = scope.TEMPLATE
temp_buffer = io.StringIO() if is_user_project:
if template == "subdirs":
# Handle top level repo project in a special way. handle_subdir(scope, cm_fh, indent=indent, is_example=True, is_user_project=True)
if is_top_level_repo_project(scope.file_absolute_path): elif template in ("app", "lib"):
create_top_level_cmake_conf() handle_app_or_lib(scope, cm_fh, indent=indent, is_example=True, is_user_project=True)
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 is_config_test_project(scope.file_absolute_path):
handle_config_test_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, temp_buffer, indent=indent, is_example=is_example)
else: else:
print(f" XXXX: {scope.file}: Template type {template} not yet supported.") temp_buffer = io.StringIO()
buffer_value = temp_buffer.getvalue() # Handle top level repo project in a special way.
if is_top_level_repo_project(scope.file_absolute_path):
create_top_level_cmake_conf()
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 is_config_test_project(scope.file_absolute_path):
handle_config_test_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, temp_buffer, indent=indent, is_example=is_example)
else:
print(f" XXXX: {scope.file}: Template type {template} not yet supported.")
if is_top_level_repo_examples_project(scope.file_absolute_path): buffer_value = temp_buffer.getvalue()
# Wrap top level examples project with some commands which
# are necessary to build examples as part of the overall
# build.
buffer_value = f"qt_examples_build_begin()\n\n{buffer_value}\nqt_examples_build_end()\n"
cm_fh.write(buffer_value) 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 = f"qt_examples_build_begin()\n\n{buffer_value}\nqt_examples_build_end()\n"
cm_fh.write(buffer_value)
def generate_new_cmakelists(scope: Scope, *, is_example: bool = False, debug: bool = False) -> None: def generate_new_cmakelists(
scope: Scope, *, is_example: bool = False, is_user_project: bool = True, debug: bool = False
) -> None:
if debug: if debug:
print("Generating CMakeLists.gen.txt") print("Generating CMakeLists.gen.txt")
with open(scope.generated_cmake_lists_path, "w") as cm_fh: with open(scope.generated_cmake_lists_path, "w") as cm_fh:
@ -4848,7 +4893,9 @@ def generate_new_cmakelists(scope: Scope, *, is_example: bool = False, debug: bo
is_example_heuristic = is_example_project(scope.file_absolute_path) is_example_heuristic = is_example_project(scope.file_absolute_path)
final_is_example_decision = is_example or is_example_heuristic final_is_example_decision = is_example or is_example_heuristic
cmakeify_scope(scope, cm_fh, is_example=final_is_example_decision) cmakeify_scope(
scope, cm_fh, is_example=final_is_example_decision, is_user_project=is_user_project
)
def do_include(scope: Scope, *, debug: bool = False) -> None: def do_include(scope: Scope, *, debug: bool = False) -> None:
@ -5044,7 +5091,12 @@ def main() -> None:
print(f'Skipping conversion of project: "{project_file_absolute_path}"') print(f'Skipping conversion of project: "{project_file_absolute_path}"')
continue continue
generate_new_cmakelists(file_scope, is_example=args.is_example, debug=args.debug) generate_new_cmakelists(
file_scope,
is_example=args.is_example,
is_user_project=args.is_user_project,
debug=args.debug,
)
copy_generated_file = True copy_generated_file = True

4
util/cmake/qmake2cmake Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
script_dir_path=`dirname $0`
script_dir_path=`(cd "$script_dir_path"; /bin/pwd)`
python3 $script_dir_path/pro2cmake.py --is-user-project $@

View File

@ -0,0 +1,2 @@
@echo off
python %~dp0\pro2cmake.py --is-user-project %*