CMake: Handle standalone config.tests in configure libraries section
Some library entries in configure.json have a test entry. An example is assimp in qtquick3d. qmake tries to find the library via the sources section, and then tries to compile the test found in config.tests/assimp/assimp.pro while automagically passing it the include and link flags it found for assimp. We didn't handle that in CMake, and now we kind of do. configurejson2cmake will now create a corresponding qt_config_compile_test call where it will pass a list of packages and libraries to find and link against. pro2cmake will in turn generate new code for the standalone config.test project. This code will iterate over packages that need to be found (like WrapAssimp) and then link against a list of passed-in targets. In this way the config.test/assimp/main.cpp file can successfully use assimp code (due to propagated include headers). qt_config_compile_test is augmented to take a new PACKAGES argument, with an example as follows PACKAGES PACKAGE Foo 6 COMPONENTS Bar PACKAGE Baz REQUIRED The arguments will be parsed and passed to the try_compile project, to call find_package() on them. We also need to pass the C/C++ standard values to the try_compile project, as well as other try_compile specific flags, like the toolchain, as given by qt_get_platform_try_compile_vars(). Change-Id: I4a3f76c75309c70c78e580b80114b33870b2cf79 Reviewed-by: Leander Beernaert <leander.beernaert@qt.io> Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
parent
de78425ca0
commit
d2931a2626
@ -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})
|
||||
|
@ -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", "")))
|
||||
|
@ -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")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user