diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index b957253fea..9e88ceac91 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -641,12 +641,74 @@ function(qt_config_compile_test name) endif() cmake_parse_arguments(arg "" "LABEL;PROJECT_PATH;C_STANDARD;CXX_STANDARD" - "COMPILE_OPTIONS;LIBRARIES;CODE" ${ARGN}) + "COMPILE_OPTIONS;LIBRARIES;CODE;PACKAGES" ${ARGN}) if(arg_PROJECT_PATH) message(STATUS "Performing Test ${arg_LABEL}") + + set(flags "") + qt_get_platform_try_compile_vars(platform_try_compile_vars) + list(APPEND flags ${platform_try_compile_vars}) + + # If the repo has its own cmake modules, include those in the module path, so that various + # find_package calls work. + if(EXISTS "${PROJECT_SOURCE_DIR}/cmake") + list(APPEND flags "-DCMAKE_MODULE_PATH:STRING=${PROJECT_SOURCE_DIR}/cmake") + endif() + + # Pass which packages need to be found. + # Very scary / ugly escaping incoming. + if(arg_PACKAGES) + set(packages_list "") + + # Parse the package names, version, etc. An example would be: + # PACKAGE Foo 6 REQUIRED + # PACKAGE Bar 2 COMPONENTS Baz + foreach(p ${arg_PACKAGES}) + if(p STREQUAL PACKAGE) + if(package_entry) + # Use 6 backslashes + ; which will be collapsed when doing variable + # expansion at multiple stages. + list(JOIN package_entry "\\\\\\;" package_entry_string) + list(APPEND packages_list "${package_entry_string}") + endif() + + set(package_entry "") + else() + list(APPEND package_entry "${p}") + endif() + endforeach() + # Parse final entry. + if(package_entry) + list(JOIN package_entry "\\\\\\;" package_entry_string) + list(APPEND packages_list "${package_entry_string}") + endif() + + # Before the join, packages_list has 3 backslashes + ; for each package part + # (name, component) if you display them. + # After the join, packages_list has 2 backslashes + ; for each package part, and a + # '\;' to separate package entries. + list(JOIN packages_list "\;" packages_list) + + # Finally when appending the joined string to the flags, the flags are separated by + # ';', the package entries by '\;', and the packages parts of an entry by '\\;'. + # Example: + # WrapFoo\\;6\\;COMPONENTS\\;bar\;WrapBaz\\;5 + list(APPEND flags "-DQT_CONFIG_COMPILE_TEST_PACKAGES:STRING=${packages_list}") + + # Inside the project, the value of QT_CONFIG_COMPILE_TEST_PACKAGES is used in a foreach + # loop that calls find_package() for each package entry, and thus the variable expansion + # ends up calling something like find_package(WrapFoo;6;COMPONENTS;bar) aka + # find_package(WrapFoo 6 COMPONENTS bar). + endif() + + # Pass which libraries need to be linked against. + if(arg_LIBRARIES) + list(APPEND flags "-DQT_CONFIG_COMPILE_TEST_LIBRARIES:STRING=${arg_LIBRARIES}") + endif() + try_compile(HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}" - "${name}") + "${name}" CMAKE_FLAGS ${flags}) if(${HAVE_${name}}) set(status_label "Success") @@ -721,6 +783,10 @@ function(qt_get_platform_try_compile_vars out_var) list(APPEND flags "VCPKG_CHAINLOAD_TOOLCHAIN_FILE") endif() + # Pass language standard flags. + list(APPEND flags "CMAKE_C_STANDARD") + list(APPEND flags "CMAKE_CXX_STANDARD") + # Assemble the list with regular options. set(flags_cmd_line "") foreach(flag ${flags}) diff --git a/util/cmake/configurejson2cmake.py b/util/cmake/configurejson2cmake.py index 5f60e55ca4..11b216dc37 100755 --- a/util/cmake/configurejson2cmake.py +++ b/util/cmake/configurejson2cmake.py @@ -499,6 +499,70 @@ def parseInput(ctx, sinput, data, cm_fh): return +def get_library_usage_for_compile_test(library): + result = {} + mapped_library = find_3rd_party_library_mapping(library) + if not mapped_library: + result["fixme"] = f"# FIXME: use: unmapped library: {library}\n" + return result + + if mapped_library.test_library_overwrite: + target_name = mapped_library.test_library_overwrite + else: + target_name = mapped_library.targetName + result["target_name"] = target_name + result["package_name"] = mapped_library.packageName + result["extra"] = mapped_library.extra + return result + + +# Handles config.test/foo/foo.pro projects. +def write_standalone_compile_test(cm_fh, ctx, data, config_test_name, is_library_test): + rel_test_project_path = f"{ctx['test_dir']}/{config_test_name}" + if posixpath.exists(f"{ctx['project_dir']}/{rel_test_project_path}/CMakeLists.txt"): + label = "" + libraries = [] + packages = [] + + if "label" in data: + label = data["label"] + + if is_library_test and config_test_name in data["libraries"]: + if "label" in data["libraries"][config_test_name]: + label = data["libraries"][config_test_name]["label"] + + # If a library entry in configure.json has a test, and + # the test uses a config.tests standalone project, we + # need to get the package and target info for the + # library, and pass it to the test so compiling and + # linking succeeds. + library_usage = get_library_usage_for_compile_test(config_test_name) + if "target_name" in library_usage: + libraries.append(library_usage["target_name"]) + if "package_name" in library_usage: + find_package_arguments = [] + find_package_arguments.append(library_usage["package_name"]) + if "extra" in library_usage: + find_package_arguments.extend(library_usage["extra"]) + package_line = "PACKAGE " + " ".join(find_package_arguments) + packages.append(package_line) + + cm_fh.write( + f""" +qt_config_compile_test("{config_test_name}" + LABEL "{label}" + PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{rel_test_project_path}" +""" + ) + if libraries: + libraries_string = " ".join(libraries) + cm_fh.write(f" LIBRARIES {libraries_string}\n") + if packages: + packages_string = " ".join(packages) + cm_fh.write(f" PACKAGES {packages_string}") + cm_fh.write(f")\n") + + def write_compile_test( ctx, name, details, data, cm_fh, manual_library_list=None, is_library_test=False ): @@ -514,15 +578,7 @@ def write_compile_test( print(f" XXXX Failed to locate inherited library test {inherited_test_name}") if isinstance(details, str): - rel_test_project_path = f"{ctx['test_dir']}/{details}" - if posixpath.exists(f"{ctx['project_dir']}/{rel_test_project_path}/CMakeLists.txt"): - cm_fh.write( - f""" -qt_config_compile_test("{details}" - LABEL "{data['label']}" - PROJECT_PATH "${{CMAKE_CURRENT_SOURCE_DIR}}/{rel_test_project_path}") -""" - ) + write_standalone_compile_test(cm_fh, ctx, data, details, is_library_test) return def resolve_head(detail): @@ -644,14 +700,12 @@ qt_config_compile_test("{details}" if len(library) == 0: continue - mapped_library = find_3rd_party_library_mapping(library) - if not mapped_library: - qmakeFixme += f"# FIXME: use: unmapped library: {library}\n" + library_usage = get_library_usage_for_compile_test(library) + if "fixme" in library_usage: + qmakeFixme += library_usage["fixme"] continue - if mapped_library.test_library_overwrite: - library_list.append(mapped_library.test_library_overwrite) else: - library_list.append(mapped_library.targetName) + library_list.append(library_usage["target_name"]) cm_fh.write(f"qt_config_compile_test({featureName(name)}\n") cm_fh.write(lineify("LABEL", data.get("label", ""))) diff --git a/util/cmake/pro2cmake.py b/util/cmake/pro2cmake.py index e496a2448d..8d1a62ddc9 100755 --- a/util/cmake/pro2cmake.py +++ b/util/cmake/pro2cmake.py @@ -3912,7 +3912,17 @@ def handle_config_test_project(scope: Scope, cm_fh: IO[str]): project_name = os.path.splitext(os.path.basename(scope.file_absolute_path))[0] content = ( f"cmake_minimum_required(VERSION 3.14.0)\n" - f"project(config_test_{project_name} LANGUAGES CXX)\n" + f"project(config_test_{project_name} LANGUAGES C CXX)\n" + """ +foreach(p ${QT_CONFIG_COMPILE_TEST_PACKAGES}) + find_package(${p}) +endforeach() + +if(QT_CONFIG_COMPILE_TEST_LIBRARIES) + link_libraries(${QT_CONFIG_COMPILE_TEST_LIBRARIES}) +endif() + +""" ) cm_fh.write(f"{content}\n")