6.5.3 clean

This commit is contained in:
kleuter 2023-11-01 18:02:52 +01:00
parent bbe896803b
commit 7018d9e6c8
2170 changed files with 57471 additions and 43550 deletions

View File

@ -8,7 +8,7 @@ if (NOT DEFINED QT_SUPERBUILD OR DEFINED QT_REPO_MODULE_VERSION)
list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
endif()
set(QT_REPO_MODULE_VERSION "6.5.1")
set(QT_REPO_MODULE_VERSION "6.5.3")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_COPYRIGHT_YEAR "2023")

2
.gitattributes vendored
View File

@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto

View File

@ -1 +1 @@
QT_PACKAGEDATE_STR=2023-05-22
QT_PACKAGEDATE_STR=2023-09-25

2
.tag
View File

@ -1 +1 @@
55aee8697512af105dfefabc1e2ec41d4df1e45e
372eaedc5b8c771c46acc4c96e91bbade4ca3624

View File

@ -40,11 +40,26 @@ function(qt_internal_check_if_path_has_symlinks path)
endwhile()
endif()
if(is_symlink)
message(FATAL_ERROR "The path \"${path}\" contains symlinks. \
This is not supported. Possible solutions:
- map directories using a transparent mechanism such as mount --bind
- pass the real path of the build directory to CMake, e.g. using \
cd $(realpath <path>) before invoking cmake <source_dir>.")
set(possible_solutions_for_resolving_symlink [[
- Map directories using a transparent mechanism such as mount --bind
- Pass the real path of the build directory to CMake, e.g. using
cd $(realpath <path>) before invoking cmake <source_dir>.
]])
if(QT_ALLOW_SYMLINK_IN_PATHS)
# In some cases, e.g., Homebrew, it is beneficial to skip this check.
# Before this, Homebrew had to patch this out to be able to get their build.
message(WARNING
"The path \"${path}\" contains symlinks. "
"This is not recommended, and it may lead to unexpected issues. If you do "
"not have a good reason for enabling 'QT_ALLOW_SYMLINK_IN_PATHS', disable "
"it, and follow one of the following solutions: \n"
"${possible_solutions_for_resolving_symlink} ")
else()
message(FATAL_ERROR
"The path \"${path}\" contains symlinks. "
"This is not supported. Possible solutions: \n"
"${possible_solutions_for_resolving_symlink} ")
endif()
endif()
endfunction()
qt_internal_check_if_path_has_symlinks("${CMAKE_BINARY_DIR}")

View File

@ -128,7 +128,8 @@ check_cxx_source_compiles("
#include <EGL/egl.h>
int main(int, char **) {
EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0;
[[maybe_unused]] EGLint x = 0;
EGLDisplay dpy = 0; EGLContext ctx = 0;
eglDestroyContext(dpy, ctx);
}" HAVE_EGL)

View File

@ -12,12 +12,24 @@ find_path(GSSAPI_INCLUDE_DIRS
HINTS ${PC_GSSAPI_INCLUDEDIR}
)
# On macOS, vcpkg opts for finding frameworks LAST. This is generally fine;
# however, in the case of GSSAPI, `usr/lib/libgssapi_krb5.tbd` which is a
# symlink to `Kerberos.framework` misses a few symols, e.g.,
# `___gss_c_nt_hostbased_service_oid_desc`, and it causes build failure.
# So, we need to make sure that we find `GSS.framework`.
set(gssapi_library_names
GSS # framework
gss # solaris
gssapi # FreeBSD
gssapi_krb5
)
if(APPLE)
list(REMOVE_ITEM gssapi_library_names "gssapi_krb5")
endif()
find_library(GSSAPI_LIBRARIES
NAMES
GSS # framework
gss # solaris
gssapi # FreeBSD
gssapi_krb5
${gssapi_library_names}
HINTS ${PC_GSSAPI_LIBDIR}
)
@ -44,4 +56,3 @@ mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
include(FeatureSummary)
set_package_properties(GSSAPI PROPERTIES
DESCRIPTION "Generic Security Services Application Program Interface")

View File

@ -18,9 +18,7 @@ find_package_handle_standard_args(PPS DEFAULT_MSG PPS_INCLUDE_DIR PPS_LIBRARY)
mark_as_advanced(PPS_INCLUDE_DIR PPS_LIBRARY)
if(PPS_FOUND)
add_library(__PPS INTERFACE IMPORTED)
target_link_libraries(__PPS INTERFACE "${PPS_LIBRARY}")
target_include_directories(__PPS INTERFACE "${PPS_INCLUDE_DIR}")
add_library(PPS::PPS ALIAS __PPS)
add_library(PPS::PPS INTERFACE IMPORTED)
target_link_libraries(PPS::PPS INTERFACE "${PPS_LIBRARY}")
target_include_directories(PPS::PPS INTERFACE "${PPS_INCLUDE_DIR}")
endif()

View File

@ -17,14 +17,18 @@ if (OpenGL_FOUND)
add_library(WrapOpenGL::WrapOpenGL INTERFACE IMPORTED)
if(APPLE)
# CMake 3.27 and older:
# On Darwin platforms FindOpenGL sets IMPORTED_LOCATION to the absolute path of the library
# within the framework. This ends up as an absolute path link flag, which we don't want,
# because that makes our .prl files un-relocatable.
# Extract the framework path instead, and use that in INTERFACE_LINK_LIBRARIES,
# which CMake ends up transforming into a reloctable -framework flag.
# which CMake ends up transforming into a relocatable -framework flag.
# See https://gitlab.kitware.com/cmake/cmake/-/issues/20871 for details.
#
# CMake 3.28 and above:
# IMPORTED_LOCATION is the absolute path the the OpenGL.framework folder.
get_target_property(__opengl_fw_lib_path OpenGL::GL IMPORTED_LOCATION)
if(__opengl_fw_lib_path)
if(__opengl_fw_lib_path AND NOT __opengl_fw_lib_path MATCHES "/([^/]+)\\.framework$")
get_filename_component(__opengl_fw_path "${__opengl_fw_lib_path}" DIRECTORY)
endif()

View File

@ -53,6 +53,7 @@ function(qt_internal_add_app target)
${arg_NO_UNITY_BUILD}
${forward_install_dir}
SOURCES ${arg_SOURCES}
NO_PCH_SOURCES ${arg_NO_PCH_SOURCES}
NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES}
INCLUDE_DIRECTORIES
${arg_INCLUDE_DIRECTORIES}

View File

@ -115,43 +115,38 @@ endfunction()
function(qt_run_linker_version_script_support)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/version_flag.map" "VERS_1 { global: sym; };
VERS_2 { global: sym; }
VERS_1;
")
if(DEFINED CMAKE_REQUIRED_FLAGS)
set(CMAKE_REQUIRED_FLAGS_SAVE ${CMAKE_REQUIRED_FLAGS})
else()
set(CMAKE_REQUIRED_FLAGS "")
endif()
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Wl,--version-script=\"${CMAKE_CURRENT_BINARY_DIR}/version_flag.map\"")
# Pass the linker that the main project uses to the version script compile test.
qt_internal_get_active_linker_flags(linker_flags)
if(linker_flags)
set(CMAKE_REQUIRED_LINK_OPTIONS ${linker_flags})
endif()
check_cxx_source_compiles("int main(void){return 0;}" HAVE_LD_VERSION_SCRIPT)
if(DEFINED CMAKE_REQUIRED_FLAGS_SAVE)
set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVE})
endif()
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/conftest.map")
# For some reason the linker command line written by the XCode generator, which is
# subsequently executed by xcodebuild, ignores the linker flag, and thus the test
# seemingly succeeds. Explicitly disable the version script test on darwin platforms.
if(APPLE)
set(HAVE_LD_VERSION_SCRIPT OFF)
endif()
# Also makes no sense with MSVC-style command-line
if(MSVC)
if(NOT APPLE AND NOT MSVC)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/version_flag.map" [=[
VERS_1 { global: sym1; };
VERS_2 { global: sym2; } VERS_1;
]=])
set(CMAKE_REQUIRED_LINK_OPTIONS "")
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS
"-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/version_flag.map")
# Pass the linker that the main project uses to the version script compile test.
qt_internal_get_active_linker_flags(linker_flags)
if(linker_flags)
list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${linker_flags})
endif()
check_cxx_source_compiles([=[
int sym1;
int sym2;
int main(void) { return 0; }
]=] HAVE_LD_VERSION_SCRIPT)
file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/version_flag.map")
else()
set(HAVE_LD_VERSION_SCRIPT OFF)
endif()
set(TEST_ld_version_script "${HAVE_LD_VERSION_SCRIPT}" CACHE INTERNAL "linker version script support")
set(TEST_ld_version_script "${HAVE_LD_VERSION_SCRIPT}"
CACHE INTERNAL "linker version script support")
list(APPEND QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT TEST_ld_version_script)
set(QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT ${QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT} CACHE INTERNAL "Test variables that should be exported")
set(QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT ${QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT}
CACHE INTERNAL "Test variables that should be exported")
endfunction()
function(qt_internal_ensure_latest_win_nt_api)

View File

@ -101,7 +101,8 @@ if(MACOS AND QT_IS_MACOS_UNIVERSAL
QT_FEATURE_x86intrin)
endif()
if(MACOS AND QT_IS_MACOS_UNIVERSAL AND __qt_osx_first_arch STREQUAL "x86_64")
if(MACOS AND QT_IS_MACOS_UNIVERSAL AND
(__qt_osx_first_arch STREQUAL "x86_64" OR __qt_osx_first_arch STREQUAL "x86_64h"))
set(QT_FORCE_FEATURE_neon ON CACHE INTERNAL "Force enable neon due to platform requirements.")
set(__QtFeature_custom_enabled_cache_variables
TEST_subarch_neon
@ -373,6 +374,10 @@ qt_copy_or_install(DIRECTORY cmake/
FILES_MATCHING PATTERN "Find*.cmake"
PATTERN "tests" EXCLUDE
PATTERN "3rdparty" EXCLUDE
PATTERN "macos" EXCLUDE
PATTERN "ios" EXCLUDE
PATTERN "platforms" EXCLUDE
PATTERN "QtBuildInternals" EXCLUDE
)
# In prefix builds we also need to copy the files into the build config directory, so that the
@ -383,6 +388,10 @@ if(QT_WILL_INSTALL)
FILES_MATCHING PATTERN "Find*.cmake"
PATTERN "tests" EXCLUDE
PATTERN "3rdparty" EXCLUDE
PATTERN "macos" EXCLUDE
PATTERN "ios" EXCLUDE
PATTERN "platforms" EXCLUDE
PATTERN "QtBuildInternals" EXCLUDE
)
endif()

View File

@ -284,21 +284,21 @@ if(WIN32)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS WIN64 _WIN64)
endif()
if(MSVC)
if (CLANG)
if(CLANG)
if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC" OR MSVC)
set(QT_DEFAULT_MKSPEC win32-clang-msvc)
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" OR MINGW)
set(QT_DEFAULT_MKSPEC win32-clang-g++)
endif()
elseif(MSVC)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(QT_DEFAULT_MKSPEC win32-arm64-msvc)
else()
set(QT_DEFAULT_MKSPEC win32-msvc)
endif()
elseif(CLANG AND MINGW)
set(QT_DEFAULT_MKSPEC win32-clang-g++)
elseif(MINGW)
set(QT_DEFAULT_MKSPEC win32-g++)
endif()
if (MINGW)
list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS MINGW_HAS_SECURE_API=1)
endif()
elseif(LINUX)
@ -376,15 +376,23 @@ else()
set(QT_QMAKE_HOST_MKSPEC "${QT_QMAKE_TARGET_MKSPEC}")
endif()
if(NOT EXISTS "${QT_MKSPECS_DIR}/${QT_QMAKE_TARGET_MKSPEC}")
if(NOT QT_QMAKE_TARGET_MKSPEC OR NOT EXISTS "${QT_MKSPECS_DIR}/${QT_QMAKE_TARGET_MKSPEC}")
if(NOT QT_QMAKE_TARGET_MKSPEC)
set(reason "Platform is not detected. Please make sure your build environment is configured"
" properly or specify it manually using QT_QMAKE_TARGET_MKSPEC variable and one of the"
" known platforms.")
else()
set(reason "Unknown platform ${QT_QMAKE_TARGET_MKSPEC}")
endif()
file(GLOB known_platforms
LIST_DIRECTORIES true
RELATIVE "${QT_MKSPECS_DIR}"
"${QT_MKSPECS_DIR}/*"
)
list(JOIN known_platforms "\n " known_platforms)
message(FATAL_ERROR "Unknown platform ${QT_QMAKE_TARGET_MKSPEC}\n\
Known platforms:\n ${known_platforms}")
message(FATAL_ERROR "${reason}\n"
"Known platforms:\n ${known_platforms}")
endif()
if(NOT DEFINED QT_DEFAULT_PLATFORM_DEFINITIONS)
@ -459,6 +467,7 @@ set(__default_private_args
DISABLE_AUTOGEN_TOOLS
ENABLE_AUTOGEN_TOOLS
PLUGIN_TYPES
NO_PCH_SOURCES
NO_UNITY_BUILD_SOURCES
)
set(__default_public_args

View File

@ -109,7 +109,7 @@ from the build directory")
set(QT_INTERNAL_BUILD_INSTRUCTIONS_SHOWN "TRUE" CACHE STRING "" FORCE)
if(QT_SUPERBUILD)
qt_internal_save_previously_found_packages()
qt_internal_save_previously_visited_packages()
endif()
endfunction()

View File

@ -308,24 +308,19 @@ function(qt_build_internals_add_toplevel_targets)
endfunction()
macro(qt_enable_cmake_languages)
include(CheckLanguage)
set(__qt_required_language_list C CXX)
set(__qt_optional_language_list )
set(__qt_platform_required_language_list )
# https://gitlab.kitware.com/cmake/cmake/-/issues/20545
if(APPLE)
list(APPEND __qt_optional_language_list OBJC OBJCXX)
list(APPEND __qt_platform_required_language_list OBJC OBJCXX)
endif()
foreach(__qt_lang ${__qt_required_language_list})
enable_language(${__qt_lang})
endforeach()
foreach(__qt_lang ${__qt_optional_language_list})
check_language(${__qt_lang})
if(CMAKE_${__qt_lang}_COMPILER)
enable_language(${__qt_lang})
endif()
foreach(__qt_lang ${__qt_platform_required_language_list})
enable_language(${__qt_lang})
endforeach()
# The qtbase call is handled in qtbase/CMakeLists.txt.
@ -434,6 +429,12 @@ macro(qt_build_repo_begin)
add_custom_target(sync_headers)
endif()
# The special target that we use to sync 3rd-party headers before the gn run when building
# qtwebengine in top-level builds.
if(NOT TARGET thirdparty_sync_headers)
add_custom_target(thirdparty_sync_headers)
endif()
# Add global qt_plugins, qpa_plugins and qpa_default_plugins convenience custom targets.
# Internal executables will add a dependency on the qpa_default_plugins target,
# so that building and running a test ensures it won't fail at runtime due to a missing qpa
@ -562,7 +563,7 @@ macro(qt_build_repo_end)
endif()
if(NOT QT_SUPERBUILD)
qt_internal_save_previously_found_packages()
qt_internal_save_previously_visited_packages()
endif()
if(QT_INTERNAL_FRESH_REQUESTED)
@ -1415,3 +1416,13 @@ function(qt_internal_run_common_config_tests)
qt_internal_check_cmp0099_available()
qt_configure_end_summary_section()
endfunction()
# It is used in QtWebEngine to replace the REALPATH with ABSOLUTE path, which is
# useful for building Qt in Homebrew.
function(qt_internal_get_filename_path_mode out_var)
set(mode REALPATH)
if(APPLE AND QT_ALLOW_SYMLINK_IN_PATHS)
set(mode ABSOLUTE)
endif()
set(${out_var} ${mode} PARENT_SCOPE)
endfunction()

View File

@ -124,5 +124,5 @@ endif()
# Emscripten Clang
if(WASM)
set(QT_CFLAGS_OPTIMIZE_DEBUG "-O2 -g") # -Og is not supported
set(QT_CFLAGS_SSE2 -O2 -msimd128 -msse -msse2)
set(QT_CFLAGS_SSE2 "-O2 -msimd128 -msse -msse2")
endif()

View File

@ -30,6 +30,7 @@ function(qt_internal_add_executable name)
endif()
_qt_internal_create_executable(${name})
qt_internal_mark_as_internal_target(${name})
if(ANDROID)
_qt_internal_android_executable_finalizer(${name})
endif()
@ -119,6 +120,7 @@ function(qt_internal_add_executable name)
qt_internal_extend_target("${name}"
${arg_NO_UNITY_BUILD}
SOURCES ${arg_SOURCES}
NO_PCH_SOURCES ${arg_NO_PCH_SOURCES}
NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES}
INCLUDE_DIRECTORIES ${private_includes}
DEFINES ${arg_DEFINES}
@ -369,6 +371,7 @@ function(qt_internal_add_configure_time_executable target)
set(target_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/configure_time_bins")
if(arg_CONFIG)
set(CMAKE_TRY_COMPILE_CONFIGURATION "${arg_CONFIG}")
string(TOUPPER "_${arg_CONFIG}" config_suffix)
endif()
get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
@ -462,6 +465,29 @@ function(qt_internal_add_configure_time_executable target)
set(cmake_flags_arg CMAKE_FLAGS "${arg_CMAKE_FLAGS}")
endif()
configure_file("${template}" "${target_binary_dir}/CMakeLists.txt" @ONLY)
qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages)
foreach(lang IN LISTS enabled_languages)
set(compiler_flags_var "CMAKE_${lang}_FLAGS")
list(APPEND cmake_flags_arg "-D${compiler_flags_var}:STRING=${${compiler_flags_var}}")
if(arg_CONFIG)
set(compiler_flags_var_config "${compiler_flags_var}${config_suffix}")
list(APPEND cmake_flags_arg
"-D${compiler_flags_var_config}:STRING=${${compiler_flags_var_config}}")
endif()
endforeach()
qt_internal_get_target_link_types_for_flag_manipulation(target_link_types)
foreach(linker_type IN LISTS target_link_types)
set(linker_flags_var "CMAKE_${linker_type}_LINKER_FLAGS")
list(APPEND cmake_flags_arg "-D${linker_flags_var}:STRING=${${linker_flags_var}}")
if(arg_CONFIG)
set(linker_flags_var_config "${linker_flags_var}${config_suffix}")
list(APPEND cmake_flags_arg
"-D${linker_flags_var_config}:STRING=${${linker_flags_var_config}}")
endif()
endforeach()
try_compile(result
"${target_binary_dir}"
"${target_binary_dir}"

View File

@ -41,10 +41,13 @@ macro(qt_find_package)
# Due to this behavior being different from what general CMake projects expect, it is only
# done for -developer-builds.
if(QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES AND
NOT "${ARGV0}" IN_LIST QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES)
NOT "${ARGV0}" IN_LIST QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES
AND "${ARGV0}" IN_LIST QT_INTERNAL_PREVIOUSLY_SEARCHED_PACKAGES)
set(_qt_find_package_skip_find_package TRUE)
endif()
set_property(GLOBAL APPEND PROPERTY _qt_previously_searched_packages "${ARGV0}")
if(QT_DEBUG_QT_FIND_PACKAGE AND ${ARGV0}_FOUND AND arg_PROVIDED_TARGETS)
set(_qt_find_package_skip_find_package TRUE)
foreach(qt_find_package_target_name ${arg_PROVIDED_TARGETS})
@ -221,7 +224,7 @@ endmacro()
# Only applies to -developer-builds by default.
# Can also be opted in or opted out via QT_INTERNAL_SAVE_PREVIOUSLY_FOUND_PACKAGES.
# Opting out will need two reconfigurations to take effect.
function(qt_internal_save_previously_found_packages)
function(qt_internal_save_previously_visited_packages)
if(DEFINED QT_INTERNAL_SAVE_PREVIOUSLY_FOUND_PACKAGES)
set(should_save "${QT_INTERNAL_SAVE_PREVIOUSLY_FOUND_PACKAGES}")
else()
@ -235,6 +238,7 @@ function(qt_internal_save_previously_found_packages)
if(NOT should_save)
# When the value is flipped to OFF, remove any previously saved packages.
unset(QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES CACHE)
unset(QT_INTERNAL_PREVIOUSLY_SEARCHED_PACKAGES CACHE)
return()
endif()
@ -244,6 +248,15 @@ function(qt_internal_save_previously_found_packages)
set(QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES "${_qt_previously_found_packages}" CACHE INTERNAL
"List of CMake packages found during configuration using qt_find_package.")
endif()
get_property(_qt_previously_searched_packages GLOBAL PROPERTY _qt_previously_searched_packages)
if(_qt_previously_searched_packages)
list(REMOVE_DUPLICATES _qt_previously_searched_packages)
set(QT_INTERNAL_PREVIOUSLY_SEARCHED_PACKAGES
"${_qt_previously_searched_packages}" CACHE INTERNAL
"List of CMake packages searched during configuration using qt_find_package."
)
endif()
endfunction()
# Return qmake library name for the given target, e.g. return "vulkan" for "Vulkan::Vulkan".

View File

@ -33,22 +33,15 @@ function(qt_internal_add_linker_version_script target)
endif()
string(APPEND contents "};\n")
set(current "Qt_${PROJECT_VERSION_MAJOR}")
if (QT_NAMESPACE STREQUAL "")
set(tag_symbol "qt_version_tag")
else()
set(tag_symbol "qt_version_tag_${QT_NAMESPACE}")
endif()
string(APPEND contents "${current} { *; };\n")
foreach(minor_version RANGE ${PROJECT_VERSION_MINOR})
set(previous "${current}")
set(current "Qt_${PROJECT_VERSION_MAJOR}.${minor_version}")
if (minor_version EQUAL ${PROJECT_VERSION_MINOR})
string(APPEND contents "${current} { ${tag_symbol}; } ${previous};\n")
else()
string(APPEND contents "${current} {} ${previous};\n")
endif()
endforeach()
get_target_property(type ${target} TYPE)
if(NOT target_type STREQUAL "INTERFACE_LIBRARY")
set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>")
set(check_genex "$<BOOL:${property_genex}>")
string(APPEND contents
"$<${check_genex}:${property_genex}>")
endif()
set(infile "${CMAKE_CURRENT_BINARY_DIR}/${target}.version.in")
set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${target}.version")
@ -87,6 +80,11 @@ function(qt_internal_add_link_flags_no_undefined target)
if (NOT QT_BUILD_SHARED_LIBS OR WASM)
return()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
# ld64 defaults to -undefined,error, and in Xcode 15
# passing this option is deprecated, causing a warning.
return()
endif()
if ((GCC OR CLANG) AND NOT MSVC)
if(CLANG AND QT_FEATURE_sanitizer)
return()
@ -124,11 +122,20 @@ endfunction()
function(qt_internal_apply_gc_binaries target visibility)
set(possible_visibilities PRIVATE INTERFACE PUBLIC)
list(FIND possible_visibilities "${visibility}" known_visibility)
if (known_visibility EQUAL "-1")
if(NOT visibility IN_LIST possible_visibilities)
message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.")
endif()
string(JOIN "" clang_or_gcc_begin
"$<$<OR:"
"$<CXX_COMPILER_ID:GNU>,"
"$<CXX_COMPILER_ID:Clang>,"
"$<CXX_COMPILER_ID:AppleClang>,"
"$<CXX_COMPILER_ID:IntelLLVM>"
">:"
)
set(clang_or_gcc_end ">")
if ((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC)
if(APPLE)
set(gc_sections_flag "-Wl,-dead_strip")
@ -137,16 +144,26 @@ function(qt_internal_apply_gc_binaries target visibility)
elseif(LINUX OR BSD OR WIN32 OR ANDROID)
set(gc_sections_flag "-Wl,--gc-sections")
endif()
# Save the flag value with and without genex wrapping, so we can remove the wrapping
# when generating .pc pkgconfig files.
set_property(GLOBAL PROPERTY _qt_internal_gc_sections_without_genex "${gc_sections_flag}")
set(gc_sections_flag
"${clang_or_gcc_begin}${gc_sections_flag}${clang_or_gcc_end}")
set_property(GLOBAL PROPERTY _qt_internal_gc_sections_with_genex "${gc_sections_flag}")
endif()
if(gc_sections_flag)
target_link_options("${target}" ${visibility} "${gc_sections_flag}")
endif()
if((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC)
set(split_sections_flags "-ffunction-sections" "-fdata-sections")
set(split_sections_flags
"${clang_or_gcc_begin}-ffunction-sections;-fdata-sections${clang_or_gcc_end}")
endif()
if(split_sections_flags)
target_compile_options("${target}" ${visibility} ${split_sections_flags})
target_compile_options("${target}" ${visibility} "${split_sections_flags}")
endif()
endfunction()
@ -156,13 +173,17 @@ function(qt_internal_apply_intel_cet target visibility)
endif()
set(possible_visibilities PRIVATE INTERFACE PUBLIC)
list(FIND possible_visibilities "${visibility}" known_visibility)
if (known_visibility EQUAL "-1")
if(NOT visibility IN_LIST possible_visibilities)
message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.")
endif()
if(GCC)
set(flags "-mshstk")
string(JOIN "" flags
"$<$<OR:"
"$<CXX_COMPILER_ID:GNU>,"
"$<CXX_COMPILER_ID:Clang>,"
"$<CXX_COMPILER_ID:AppleClang>"
">:-mshstk>")
endif()
if(flags)
target_compile_options("${target}" ${visibility} "${flags}")
@ -287,14 +308,15 @@ function(qt_set_msvc_cplusplus_options target visibility)
# Check qt_config_compile_test for more info.
if(MSVC AND MSVC_VERSION GREATER_EQUAL 1913)
set(flags "-Zc:__cplusplus" "-permissive-")
target_compile_options("${target}" ${visibility} "$<$<COMPILE_LANGUAGE:CXX>:${flags}>")
target_compile_options("${target}" ${visibility}
"$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<COMPILE_LANGUAGE:CXX>>:${flags}>")
endif()
endfunction()
function(qt_enable_utf8_sources target)
set(utf8_flags "")
if(MSVC)
list(APPEND utf8_flags "-utf-8")
list(APPEND utf8_flags "$<$<CXX_COMPILER_ID:MSVC>:-utf-8>")
endif()
if(utf8_flags)
@ -557,12 +579,20 @@ endfunction()
# Removes specified flags from CMAKE_<LANGUAGES>_FLAGS[_CONFIGS] variables
#
# IN_CACHE enables flags removal from CACHE
# CONFIGS list of configurations that need to clear flags. Clears all configs by default if not
# specified.
# LANGUAGES list of LANGUAGES that need clear flags. Clears all languages by default if not
# specified.
# REGEX enables the flag processing as a regular expression.
# Option Arguments:
# IN_CACHE
# Enables flags removal from CACHE
# REGEX
# Enables the flag processing as a regular expression.
#
# Multi-value Arguments:
# CONFIGS
# List of configurations that need to clear flags. Clears all configs by default if not
# specified.
#
# LANGUAGES
# List of LANGUAGES that need clear flags. Clears all languages by default if not
# specified.
function(qt_internal_remove_compiler_flags flags)
cmake_parse_arguments(PARSE_ARGV 1 arg
"IN_CACHE;REGEX"
@ -585,8 +615,7 @@ function(qt_internal_remove_compiler_flags flags)
if(arg_CONFIGS)
set(configs "${arg_CONFIGS}")
else()
message(FATAL_ERROR
"You must specify at least one configuration for which to remove the flags.")
qt_internal_get_configs_for_flag_manipulation(configs)
endif()
if(arg_REGEX)
@ -992,14 +1021,42 @@ function(qt_internal_set_up_config_optimizations_like_in_qmake)
IN_CACHE)
endif()
# Legacy Android toolchain file adds the `-g` flag to CMAKE_<LANG>_FLAGS, as a
# result, our release build ends up containing debug symbols. To avoid that, we
# remove the flag from CMAKE_<LANGL>_FLAGS and add
# it to CMAKE_<LANG>_FLAGS_DEBUG.
#
# Note:
# The new `android.toolchain.cmake` file does not have this problem, but
# it has other issues, eg., https://github.com/android/ndk/issues/1693, so we
# cannot force it. While we do load the new toolchain, it automatically falls
# back to the legacy toolchain, ie., `android-legacy.toolchain.cmake` which
# has the problem described above.
#
# Todo:
# When the new toolchain is fixed, and it doesn't fall back to the legacy
# anymore by default, then we should be able to remove this workaround.
if(ANDROID AND ANDROID_COMPILER_FLAGS MATCHES "(^| )-g")
qt_internal_remove_compiler_flags("-g")
qt_internal_add_compiler_flags(FLAGS "-g" CONFIGS DEBUG RELWITHDEBINFO)
endif()
# Update all relevant flags in the calling scope
foreach(config ${configs})
foreach(lang ${enabled_languages})
foreach(lang ${enabled_languages})
set(flag_var_name "CMAKE_${lang}_FLAGS")
set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE)
foreach(config ${configs})
set(flag_var_name "CMAKE_${lang}_FLAGS_${config}")
set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE)
endforeach()
endforeach()
foreach(t ${target_link_types})
foreach(t ${target_link_types})
set(flag_var_name "CMAKE_${t}_LINKER_FLAGS")
set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE)
foreach(config ${configs})
set(flag_var_name "CMAKE_${t}_LINKER_FLAGS_${config}")
set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE)
endforeach()

View File

@ -185,6 +185,9 @@ function(qt_internal_add_headersclean_target module_target module_headers)
# <windows.h> and <GL.h> violate the standards.
set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -WX -W3)
# Because we now add `-DNOMINMAX` to `PlatformCommonInternal`.
set(hcleanUDEFS -UNOMINMAX)
# cl.exe needs a source path
get_filename_component(source_path "${QT_MKSPECS_DIR}/features/data/dummy.cpp" REALPATH)
@ -195,6 +198,7 @@ function(qt_internal_add_headersclean_target module_target module_headers)
"${hcleanFLAGS}"
"${target_includes_joined_genex}"
"${hcleanDEFS}"
"${hcleanUDEFS}"
)
string(JOIN " " compiler_command_line_variables
"-FI"
@ -227,7 +231,7 @@ function(qt_internal_add_headersclean_target module_target module_headers)
file(GENERATE OUTPUT "${headers_check_parameters}"
CONTENT "${headers_check_parameters_content}")
set(sync_headers_dep "sync_headers")
set(sync_headers_dep "${module_target}_sync_headers")
foreach(header ${hclean_headers})
# We need realpath here to make sure path starts with drive letter

View File

@ -102,7 +102,7 @@ function(qt_internal_add_global_definition definition)
set(optional_args)
set(single_value_args VALUE)
set(multi_value_args SCOPE)
cmake_parse_arguments(args
cmake_parse_arguments(arg
"${optional_args}"
"${single_value_args}"
"${multi_value_args}"
@ -168,6 +168,8 @@ if(WIN32)
# Needed for M_PI define. Same as mkspecs/features/qt_module.prf.
# It's set for every module being built, but it's not propagated to user apps.
target_compile_definitions(PlatformModuleInternal INTERFACE _USE_MATH_DEFINES)
# Not disabling min/max macros may result in unintended substitutions of std::min/max
target_compile_definitions(PlatformCommonInternal INTERFACE NOMINMAX)
endif()
if(FEATURE_largefile AND UNIX)
target_compile_definitions(PlatformCommonInternal
@ -205,6 +207,14 @@ function(qt_internal_apply_bitcode_flags target)
target_compile_options("${target}" INTERFACE ${bitcode_flags})
endfunction()
# Function guards linker options that are applicable for internal Qt targets only from propagating
# them to user projects.
function(qt_internal_platform_link_options target scope)
set(options ${ARGN})
set(is_internal_target_genex "$<BOOL:$<TARGET_PROPERTY:_qt_is_internal_target>>")
target_link_options(${target} ${scope} "$<${is_internal_target_genex}:${options}>")
endfunction()
# Apple deprecated the entire OpenGL API in favor of Metal, which
# we are aware of, so silence the deprecation warnings in code.
# This does not apply to user-code, which will need to silence
@ -283,7 +293,7 @@ if (MSVC)
$<$<NOT:$<CONFIG:Debug>>:-guard:cf -Gw>
)
target_link_options(PlatformCommonInternal INTERFACE
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE
-DYNAMICBASE -NXCOMPAT -LARGEADDRESSAWARE
$<$<NOT:$<CONFIG:Debug>>:-OPT:REF -OPT:ICF -GUARD:CF>
)
@ -299,7 +309,7 @@ endif()
if(QT_FEATURE_intelcet)
if(MSVC)
target_link_options(PlatformCommonInternal INTERFACE
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE
-CETCOMPAT
)
else()
@ -328,30 +338,31 @@ endif()
if(DEFINED QT_EXTRA_FRAMEWORKPATHS AND APPLE)
list(TRANSFORM QT_EXTRA_FRAMEWORKPATHS PREPEND "-F" OUTPUT_VARIABLE __qt_fw_flags)
target_compile_options(PlatformCommonInternal INTERFACE ${__qt_fw_flags})
target_link_options(PlatformCommonInternal INTERFACE ${__qt_fw_flags})
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE ${__qt_fw_flags})
unset(__qt_fw_flags)
endif()
qt_internal_get_active_linker_flags(__qt_internal_active_linker_flags)
if(__qt_internal_active_linker_flags)
target_link_options(PlatformCommonInternal INTERFACE "${__qt_internal_active_linker_flags}")
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE
"${__qt_internal_active_linker_flags}")
endif()
unset(__qt_internal_active_linker_flags)
if(QT_FEATURE_enable_gdb_index)
target_link_options(PlatformCommonInternal INTERFACE "-Wl,--gdb-index")
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE "-Wl,--gdb-index")
endif()
if(QT_FEATURE_enable_new_dtags)
target_link_options(PlatformCommonInternal INTERFACE "-Wl,--enable-new-dtags")
qt_internal_platform_link_options(PlatformCommonInternal INTERFACE "-Wl,--enable-new-dtags")
endif()
function(qt_get_implicit_sse2_genex_condition out_var)
set(is_shared_lib "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>")
set(is_static_lib "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>")
set(is_static_qt_build "$<NOT:$<BOOL:${QT_BUILD_SHARED_LIBS}>>")
set(is_staitc_lib_during_static_qt_build "$<AND:${is_static_qt_build},${is_static_lib}>")
set(enable_sse2_condition "$<OR:${is_shared_lib},${is_staitc_lib_during_static_qt_build}>")
set(is_static_lib_during_static_qt_build "$<AND:${is_static_qt_build},${is_static_lib}>")
set(enable_sse2_condition "$<OR:${is_shared_lib},${is_static_lib_during_static_qt_build}>")
set(${out_var} "${enable_sse2_condition}" PARENT_SCOPE)
endfunction()

View File

@ -4,6 +4,10 @@
# This function can be used to compile java sources into a jar package.
function(qt_internal_add_jar target)
set(options)
set(oneValueArgs OUTPUT_DIR)
set(multiValueArgs INCLUDE_JARS SOURCES)
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
set(javac_target_version "${QT_ANDROID_JAVAC_TARGET}")
if (NOT javac_target_version)
@ -18,4 +22,8 @@ function(qt_internal_add_jar target)
set(CMAKE_JAVA_COMPILE_FLAGS -source "${javac_source_version}" -target "${javac_target_version}" -Xlint:unchecked -bootclasspath "${QT_ANDROID_JAR}")
add_jar(${ARGV})
foreach(f IN LISTS arg_SOURCES)
_qt_internal_expose_source_file_to_ide(${target} "${f}")
endforeach()
endfunction()

View File

@ -37,7 +37,6 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi
QMAKE_MODULE_CONFIG
EXTRA_CMAKE_FILES
EXTRA_CMAKE_INCLUDES
NO_PCH_SOURCES
EXTERNAL_HEADERS
POLICIES
${__default_private_args}
@ -447,6 +446,8 @@ function(qt_internal_add_module target)
# If EXTERNAL_HEADERS_DIR is set we install the specified directory and keep the structure
# without taking into the account the CMake source tree and syncqt outputs.
if(arg_EXTERNAL_HEADERS_DIR)
set_property(TARGET ${target}
PROPERTY _qt_external_headers_dir "${arg_EXTERNAL_HEADERS_DIR}")
qt_install(DIRECTORY "${arg_EXTERNAL_HEADERS_DIR}/"
DESTINATION "${module_install_interface_include_dir}"
)
@ -1186,10 +1187,13 @@ function(qt_internal_collect_module_headers out_var target)
get_target_property(target_type ${target} TYPE)
if(target_type STREQUAL "INTERFACE_LIBRARY")
set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}")
set(binary_dir "${CMAKE_CURRENT_BINARY_DIR}")
else()
get_target_property(source_dir ${target} SOURCE_DIR)
get_target_property(binary_dir ${target} BINARY_DIR)
endif()
get_filename_component(source_dir "${source_dir}" ABSOLUTE)
get_filename_component(binary_dir "${binary_dir}" ABSOLUTE)
get_target_property(is_3rdparty_library ${target} _qt_module_is_3rdparty_header_library)
@ -1232,7 +1236,14 @@ function(qt_internal_collect_module_headers out_var target)
"\nCondition:\n ${condition_string}")
endif()
if(file_path MATCHES "3rdparty/.+" AND NOT is_3rdparty_library)
if(is_outside_module_source_dir)
set(base_dir "${binary_dir}")
else()
set(base_dir "${source_dir}")
endif()
file(RELATIVE_PATH file_path_rel "${base_dir}" "${file_path}")
if(file_path_rel MATCHES "3rdparty/.+" AND NOT is_3rdparty_library)
set(is_3rdparty_header TRUE)
else()
set(is_3rdparty_header FALSE)

View File

@ -51,6 +51,17 @@ function(qt_internal_generate_pkg_config_file module)
list(TRANSFORM loose_include_dirs REPLACE "${INSTALL_INCLUDEDIR}" "\${includedir}")
list(TRANSFORM loose_include_dirs REPLACE "${INSTALL_MKSPECSDIR}" "\${mkspecsdir}")
# Remove genex wrapping around gc_sections flag because we can't evaluate genexes like
# $<CXX_COMPILER_ID> in file(GENERATE). And given that .pc files don't support dynamic
# evaluation like the $<CXX_COMPILER_ID> genex, distros will be expected to patch the .pc
# files according to which compiler they intend to be used with.
get_property(gc_sections_with_genex GLOBAL PROPERTY _qt_internal_gc_sections_with_genex)
get_property(gc_sections_without_genex GLOBAL PROPERTY _qt_internal_gc_sections_without_genex)
if(loose_link_options AND gc_sections_with_genex AND gc_sections_without_genex)
string(REPLACE "${gc_sections_with_genex}" "${gc_sections_without_genex}"
loose_link_options "${loose_link_options}")
endif()
qt_internal_set_pkg_config_cpp_flags(link_options "${loose_link_options}" "")
qt_internal_set_pkg_config_cpp_flags(compile_defs "${loose_compile_defs}" -D)
qt_internal_set_pkg_config_cpp_flags(include_dirs "${loose_include_dirs}" -I)

View File

@ -336,6 +336,8 @@ function(qt_internal_add_plugin target)
qt_internal_extend_target("${target}"
${arg_NO_UNITY_BUILD}
SOURCES ${arg_SOURCES}
NO_PCH_SOURCES
${arg_NO_PCH_SOURCES}
NO_UNITY_BUILD_SOURCES
${arg_NO_UNITY_BUILD_SOURCES}
INCLUDE_DIRECTORIES

View File

@ -26,9 +26,14 @@ function(qt_generate_qmake_libraries_pri_content module_name output_root_dir out
set(lib_incdir "")
set(lib_libdir "")
set(lib_libs "")
set(seen_targets "")
while(lib_targets)
list(POP_BACK lib_targets lib_target)
if(TARGET ${lib_target})
if(${lib_target} IN_LIST seen_targets)
continue()
endif()
list(APPEND seen_targets ${lib_target})
get_target_property(lib_target_type ${lib_target} TYPE)
if(lib_target_type STREQUAL "INTERFACE_LIBRARY")
get_target_property(iface_libs ${lib_target} INTERFACE_LINK_LIBRARIES)

View File

@ -55,7 +55,10 @@ if("${MODULE_ROOT}" STREQUAL "")
set(qtbase_or_top_level_build TRUE)
else()
# If MODULE_ROOT is passed without drive letter, we try to add it to the path.
get_filename_component(MODULE_ROOT "." REALPATH BASE_DIR "${MODULE_ROOT}")
# The check is necessary; otherwise, `get_filename_component` returns an empty string.
if(NOT MODULE_ROOT STREQUAL ".")
get_filename_component(MODULE_ROOT "." REALPATH BASE_DIR "${MODULE_ROOT}")
endif()
set(qtbase_or_top_level_build FALSE)
endif()
set(configure_filename "configure.cmake")
@ -136,13 +139,6 @@ while(NOT "${configure_args}" STREQUAL "")
list(POP_FRONT configure_args version)
is_valid_qt_hex_version("${arg}" "${version}")
push("-DQT_DISABLE_DEPRECATED_UP_TO=${version}")
elseif(arg STREQUAL "-unity-build")
push("-DQT_UNITY_BUILD=ON")
# QT_UNITY_BUILD_BATCH_SIZE will be set to 8, CMake's default.
elseif(arg STREQUAL "-unity-build-batch-size")
list(POP_FRONT configure_args unity_build_batch_size)
is_non_empty_valid_arg("${arg}" "${unity_build_batch_size}")
push("-DQT_UNITY_BUILD_BATCH_SIZE=${unity_build_batch_size}")
elseif(arg STREQUAL "--")
# Everything after this argument will be passed to CMake verbatim.
list(APPEND cmake_args "${configure_args}")
@ -827,6 +823,8 @@ endfunction()
drop_input(commercial)
drop_input(confirm-license)
translate_boolean_input(precompile_header BUILD_WITH_PCH)
translate_boolean_input(unity_build QT_UNITY_BUILD)
translate_string_input(unity_build_batch_size QT_UNITY_BUILD_BATCH_SIZE)
translate_boolean_input(ccache QT_USE_CCACHE)
translate_boolean_input(shared BUILD_SHARED_LIBS)
translate_boolean_input(warnings_are_errors WARNINGS_ARE_ERRORS)

View File

@ -221,7 +221,7 @@ function(_qt_internal_find_ios_development_team_id out_var)
endif()
endfunction()
function(_qt_internal_get_ios_bundle_identifier_prefix out_var)
function(_qt_internal_get_apple_bundle_identifier_prefix out_var)
get_property(prefix GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix)
get_property(prefix_computed GLOBAL PROPERTY
_qt_internal_ios_bundle_identifier_prefix_computed)
@ -269,8 +269,8 @@ function(_qt_internal_escape_rfc_1034_identifier value out_var)
set("${out_var}" "${value}" PARENT_SCOPE)
endfunction()
function(_qt_internal_get_default_ios_bundle_identifier out_var)
_qt_internal_get_ios_bundle_identifier_prefix(prefix)
function(_qt_internal_get_default_apple_bundle_identifier target out_var)
_qt_internal_get_apple_bundle_identifier_prefix(prefix)
if(NOT prefix)
set(prefix "com.yourcompany")
@ -281,14 +281,16 @@ function(_qt_internal_get_default_ios_bundle_identifier out_var)
string(SHA1 hash "${team_id}")
string(SUBSTRING "${hash}" 0 8 infix)
string(APPEND prefix ".${infix}")
else()
endif()
if(CMAKE_GENERATOR STREQUAL "Xcode")
message(WARNING
"No organization bundle identifier prefix could be retrieved from Xcode "
"preferences. This can lead to code signing issues due to a non-unique bundle "
"identifier. Please set up an organization prefix by creating a new project within "
"Xcode, or consider providing a custom bundle identifier by specifying the "
"XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER property."
)
"No organization bundle identifier prefix could be retrieved from Xcode preferences. \
This can lead to code signing issues due to a non-unique bundle \
identifier. Please set up an organization prefix by creating a new project within \
Xcode, or consider providing a custom bundle identifier by specifying the \
MACOSX_BUNDLE_GUI_IDENTIFIER or XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER property."
)
endif()
endif()
@ -299,7 +301,12 @@ function(_qt_internal_get_default_ios_bundle_identifier out_var)
# that the identifier is invalid.
_qt_internal_escape_rfc_1034_identifier("${prefix}" prefix)
set(identifier "${prefix}.\${PRODUCT_NAME:rfc1034identifier}")
if(CMAKE_GENERATOR STREQUAL "Xcode")
set(identifier "${prefix}.$(PRODUCT_NAME:rfc1034identifier)")
else()
set(identifier "${prefix}.${target}")
endif()
set("${out_var}" "${identifier}" PARENT_SCOPE)
endfunction()
@ -384,56 +391,72 @@ function(_qt_internal_set_xcode_development_team_id target)
endif()
endfunction()
function(_qt_internal_set_xcode_bundle_identifier target)
function(_qt_internal_set_apple_bundle_identifier target)
# Skip all logic if requested.
if(QT_NO_SET_XCODE_BUNDLE_IDENTIFIER)
return()
endif()
# There are two fields to consider: the CFBundleIdentifier key (CFBI) to be written to
# Info.plist
# and the PRODUCT_BUNDLE_IDENTIFIER (PBI) property to set in the Xcode project.
# The following logic enables the best out-of-the-box experience combined with maximum
# customization.
# 1) If values for both fields are not provided, assign ${PRODUCT_BUNDLE_IDENTIFIER} to CFBI
# (which is expanded by xcodebuild at build time and will use the value of PBI) and
# auto-compute a default PBI from Xcode's ${PRODUCT_NAME}.
# 2) If CFBI is set and PBI isn't, use given CFBI and keep PBI empty.
# 3) If PBI is set and CFBI isn't, assign ${PRODUCT_BUNDLE_IDENTIFIER} to CFBI and use
# the given PBI.
# 4) If both are set, use both given values.
# TLDR:
# cfbi pbi -> result_cfbi result_pbi
# unset unset computed computed
# set unset given_val unset
# unset set computed given_val
# set set given_val given_val
# There are two fields to consider: the CFBundleIdentifier key (ie., cmake_bundle_identifier)
# to be written to Info.plist and the PRODUCT_BUNDLE_IDENTIFIER (ie., xcode_bundle_identifier)
# property to set in the Xcode project. The `cmake_bundle_identifier` set by
# MACOSX_BUNDLE_GUI_IDENTIFIER applies to both Xcode, and other generators, while
# `xcode_bundle_identifier` set by XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER is
# Xcode specific.
#
# If Ninja is the generator, we set the value of `MACOSX_BUNDLE_GUI_IDENTIFIER`
# and don't touch the `XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER`.
# If Xcode is the generator, we set the value of `XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER`,
# and additionally, to silence a Xcode's warning, we set the `MACOSX_BUNDLE_GUI_IDENTIFIER` to
# `${PRODUCT_BUNDLE_IDENTIFIER}` so that Xcode could sort it out.
get_target_property(existing_cfbi "${target}" MACOSX_BUNDLE_GUI_IDENTIFIER)
if(NOT MACOSX_BUNDLE_GUI_IDENTIFIER AND NOT existing_cfbi)
set(is_cfbi_given FALSE)
else()
set(is_cfbi_given TRUE)
get_target_property(existing_cmake_bundle_identifier "${target}"
MACOSX_BUNDLE_GUI_IDENTIFIER)
get_target_property(existing_xcode_bundle_identifier "${target}"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER)
set(is_cmake_bundle_identifier_given FALSE)
if(existing_cmake_bundle_identifier)
set(is_cmake_bundle_identifier_given TRUE)
elseif(MACOSX_BUNDLE_GUI_IDENTIFIER)
set(is_cmake_bundle_identifier_given TRUE)
set(existing_cmake_bundle_identifier ${MACOSX_BUNDLE_GUI_IDENTIFIER})
endif()
if(NOT is_cfbi_given)
set(is_xcode_bundle_identifier_given FALSE)
if(existing_xcode_bundle_identifier)
set(is_xcode_bundle_identifier_given TRUE)
elseif(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER)
set(is_xcode_bundle_identifier_given TRUE)
set(existing_xcode_bundle_identifier ${CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER})
endif()
if(is_cmake_bundle_identifier_given
AND is_xcode_bundle_identifier_given
AND NOT existing_cmake_bundle_identifier STREQUAL existing_xcode_bundle_identifier)
message(WARNING
"MACOSX_BUNDLE_GUI_IDENTIFIER and XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "
"are set to different values. You only need to set one of them. ")
endif()
if(NOT is_xcode_bundle_identifier_given
AND NOT is_cmake_bundle_identifier_given)
_qt_internal_get_default_apple_bundle_identifier("${target}" bundle_id)
elseif(is_cmake_bundle_identifier_given)
set(bundle_id ${existing_cmake_bundle_identifier})
elseif(is_xcode_bundle_identifier_given)
set(bundle_id ${existing_xcode_bundle_identifier})
endif()
if(CMAKE_GENERATOR STREQUAL "Xcode")
set_target_properties("${target}"
PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER "\${PRODUCT_BUNDLE_IDENTIFIER}")
endif()
get_target_property(existing_pbi "${target}" XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER)
if(NOT CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER AND NOT existing_pbi)
set(is_pbi_given FALSE)
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${bundle_id}"
MACOSX_BUNDLE_GUI_IDENTIFIER "$(PRODUCT_BUNDLE_IDENTIFIER)")
else()
set(is_pbi_given TRUE)
endif()
if(NOT is_pbi_given AND NOT is_cfbi_given)
_qt_internal_get_default_ios_bundle_identifier(bundle_id)
set_target_properties("${target}"
PROPERTIES
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${bundle_id}")
MACOSX_BUNDLE_GUI_IDENTIFIER "${bundle_id}")
endif()
endfunction()
@ -503,7 +526,7 @@ function(_qt_internal_set_xcode_bundle_name target)
if(CMAKE_GENERATOR STREQUAL Xcode)
set_target_properties("${target}"
PROPERTIES
MACOSX_BUNDLE_BUNDLE_NAME "\${PRODUCT_NAME}")
MACOSX_BUNDLE_BUNDLE_NAME "$(PRODUCT_NAME)")
else()
set_target_properties("${target}"
PROPERTIES
@ -603,12 +626,13 @@ function(_qt_internal_finalize_apple_app target)
# This affects things like the version number or application name as reported by Qt API.
if(CMAKE_GENERATOR STREQUAL "Xcode")
_qt_internal_set_xcode_development_team_id("${target}")
_qt_internal_set_xcode_bundle_identifier("${target}")
_qt_internal_set_xcode_code_sign_style("${target}")
_qt_internal_set_xcode_bundle_display_name("${target}")
_qt_internal_set_xcode_install_path("${target}")
endif()
_qt_internal_set_xcode_bundle_name("${target}")
_qt_internal_set_apple_bundle_identifier("${target}")
_qt_internal_set_placeholder_apple_bundle_version("${target}")
endfunction()

View File

@ -307,7 +307,17 @@ function(_qt_internal_set_up_static_runtime_library target)
set_property(TARGET ${target} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
elseif(MINGW)
target_link_options(${target} INTERFACE "LINKER:-Bstatic")
get_target_property(target_type ${target} TYPE)
if(target_type STREQUAL "EXECUTABLE")
set(link_option PRIVATE)
else()
set(link_option INTERFACE)
endif()
if(CLANG)
target_link_options(${target} ${link_option} "LINKER:-Bstatic")
else()
target_link_options(${target} ${link_option} "-static")
endif()
endif()
endif()
endfunction()

View File

@ -6,7 +6,7 @@
function(qt_internal_get_relative_rpath_base_token out_var)
if(APPLE)
set(rpath_rel_base "@loader_path")
elseif(LINUX OR SOLARIS OR FREEBSD OR HURD)
elseif(LINUX OR SOLARIS OR FREEBSD OR HURD OR OPENBSD)
set(rpath_rel_base "$ORIGIN")
else()
set(rpath_rel_base "NO_KNOWN_RPATH_REL_BASE")

View File

@ -201,7 +201,11 @@ endfunction()
# Enable separate debug information for the given target
function(qt_enable_separate_debug_info target installDestination)
set(flags QT_EXECUTABLE)
set(options)
if(APPLE)
set(options DSYM_OUTPUT_DIR)
else()
set(options)
endif()
set(multiopts ADDITIONAL_INSTALL_ARGS)
cmake_parse_arguments(arg "${flags}" "${options}" "${multiopts}" ${ARGN})
@ -248,12 +252,20 @@ function(qt_enable_separate_debug_info target installDestination)
get_target_property(is_framework ${target} FRAMEWORK)
if(is_framework)
qt_internal_get_framework_info(fw ${target})
set(debug_info_bundle_dir "$<TARGET_BUNDLE_DIR:${target}>.${debug_info_suffix}")
set(BUNDLE_ID ${fw_name})
else()
set(debug_info_bundle_dir "$<TARGET_FILE:${target}>.${debug_info_suffix}")
set(BUNDLE_ID ${target})
endif()
if (NOT "x${arg_DSYM_OUTPUT_DIR}" STREQUAL "x")
set(debug_info_bundle_dir "${arg_DSYM_OUTPUT_DIR}/${target}")
elseif(is_framework)
set(debug_info_bundle_dir "$<TARGET_BUNDLE_DIR:${target}>")
else()
set(debug_info_bundle_dir "$<TARGET_FILE:${target}>")
endif()
set(debug_info_bundle_dir "${debug_info_bundle_dir}.${debug_info_suffix}")
set(debug_info_contents_dir "${debug_info_bundle_dir}/Contents")
set(debug_info_target_dir "${debug_info_contents_dir}/Resources/DWARF")
configure_file(

View File

@ -5,8 +5,8 @@
# Set the QT_IS_BUILDING_QT variable so we can verify whether we are building
# Qt from source
set(QT_BUILDING_QT TRUE CACHE
TYPE STRING "When this is present and set to true, it signals that we are building Qt from source.")
set(QT_BUILDING_QT TRUE CACHE BOOL
"When this is present and set to true, it signals that we are building Qt from source.")
# Pre-calculate the developer_build feature if it's set by the user via INPUT_developer_build
if(NOT FEATURE_developer_build AND INPUT_developer_build
@ -352,6 +352,8 @@ if(QT_UNITY_BUILD)
set(CMAKE_UNITY_BUILD_BATCH_SIZE "${QT_UNITY_BUILD_BATCH_SIZE}")
endif()
option(QT_ALLOW_SYMLINK_IN_PATHS "Allows symlinks in paths." OFF)
# We need to clean up QT_FEATURE_*, but only once per configuration cycle
get_property(qt_feature_clean GLOBAL PROPERTY _qt_feature_clean)
if(NOT qt_feature_clean)

View File

@ -139,7 +139,6 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
set(syncqt_args "${common_syncqt_arguments}")
list(APPEND syncqt_args
${common_syncqt_arguments}
-headers ${module_headers}
-stagingDir "${syncqt_staging_dir}"
-knownModules ${known_modules}
@ -150,6 +149,21 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
set(syncqt_args_rsp "${binary_dir_real}/${target}_syncqt_args")
qt_configure_file(OUTPUT "${syncqt_args_rsp}" CONTENT "${syncqt_args_string}")
get_target_property(external_headers_dir ${target} _qt_external_headers_dir)
if(external_headers_dir)
if(NOT IS_ABSOLUTE "${external_headers_dir}")
get_filename_component(external_headers_dir "${external_headers_dir}" ABSOLUTE)
endif()
if(EXISTS "${external_headers_dir}")
set(external_headers_dir_copy_cmd
COMMAND
${CMAKE_COMMAND}
-E copy_directory
"${external_headers_dir}"
"${module_build_interface_include_dir}"
)
endif()
endif()
add_custom_command(
OUTPUT
${syncqt_outputs}
@ -157,6 +171,7 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
${QT_CMAKE_EXPORT_NAMESPACE}::syncqt
"@${syncqt_args_rsp}"
${build_time_syncqt_arguments}
${external_headers_dir_copy_cmd}
COMMAND
${CMAKE_COMMAND} -E touch "${syncqt_timestamp}"
DEPENDS
@ -167,12 +182,22 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
"Running syncqt.cpp for module: ${module}"
VERBATIM
)
set(add_sync_headers_to_all "")
if(is_interface_lib)
set(add_sync_headers_to_all ALL)
endif()
add_custom_target(${target}_sync_headers
${add_sync_headers_to_all}
DEPENDS
${syncqt_outputs}
)
add_dependencies(sync_headers ${target}_sync_headers)
if(is_3rd_party_library)
add_dependencies(thirdparty_sync_headers ${target}_sync_headers)
endif()
# This target is required when building docs, to make all header files and their aliases
# available for qdoc.
# ${target}_sync_headers is added as dependency to make sure that
@ -185,6 +210,7 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge
COMMAND
${QT_CMAKE_EXPORT_NAMESPACE}::syncqt
"@${syncqt_all_args_rsp}"
${external_headers_dir_copy_cmd}
DEPENDS
${module_headers}
${syncqt_all_args_rsp}

View File

@ -14,6 +14,8 @@
# module, these files will raise a warning at configure time if the condition is not met.
# COMPILE_FLAGS
# Custom compilation flags.
# EXTRA_LINKER_SCRIPT_CONTENT
# Extra content that should be appended to a target linker script. Applicable for ld only.
# NO_PCH_SOURCES
# Skip the specified source files by PRECOMPILE_HEADERS feature.
function(qt_internal_extend_target target)
@ -36,6 +38,7 @@ function(qt_internal_extend_target target)
)
set(single_args
PRECOMPILED_HEADER
EXTRA_LINKER_SCRIPT_CONTENT
)
set(multi_args
${__default_public_args}
@ -44,7 +47,6 @@ function(qt_internal_extend_target target)
CONDITION
CONDITION_INDEPENDENT_SOURCES
COMPILE_FLAGS
NO_PCH_SOURCES
)
cmake_parse_arguments(PARSE_ARGV 1 arg
@ -237,6 +239,10 @@ function(qt_internal_extend_target target)
${sources_property} "${arg_CONDITION_INDEPENDENT_SOURCES}")
endif()
if(arg_EXTRA_LINKER_SCRIPT_CONTENT)
set_target_properties(${target} PROPERTIES
_qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}")
endif()
endfunction()
function(qt_is_imported_target target out_var)
@ -1001,6 +1007,15 @@ endfunction()
# Needed to allow selectively applying certain flags via PlatformXInternal targets.
function(qt_internal_mark_as_internal_library target)
set_target_properties(${target} PROPERTIES _qt_is_internal_library TRUE)
qt_internal_mark_as_internal_target(${target})
endfunction()
# Marks a target with a property that it was built using the internal Qt API (qt_internal_*) as
# opposed to it being a user project library or executable(qt_add_*, etc).
#
# Needed to allow selectively applying certain flags via PlatformXInternal targets.
function(qt_internal_mark_as_internal_target target)
set_target_properties(${target} PROPERTIES _qt_is_internal_target TRUE)
endfunction()
function(qt_internal_link_internal_platform_for_object_library target)

View File

@ -214,6 +214,7 @@ function(qt_internal_get_test_arg_definitions optional_args single_value_args mu
MANUAL
NO_BATCH
NO_INSTALL
BUNDLE_ANDROID_OPENSSL_LIBS
PARENT_SCOPE
)
set(${single_value_args}
@ -526,6 +527,21 @@ function(qt_internal_add_test name)
endif()
if (ANDROID)
if(arg_BUNDLE_ANDROID_OPENSSL_LIBS)
if(NOT OPENSSL_ROOT_DIR)
message(WARNING "The argument BUNDLE_ANDROID_OPENSSL_LIBS is set "
"but OPENSSL_ROOT_DIR parameter is not set.")
else()
if(EXISTS "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcrypto_3.so")
set_property(TARGET ${name} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS
"${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcrypto_3.so"
"${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libssl_3.so")
else()
message(STATUS "Test should bundle OpenSSL libraries but they are not found."
" This is fine if OpenSSL was built statically.")
endif()
endif()
endif()
qt_internal_android_test_arguments("${name}" test_executable extra_test_args)
set(test_working_dir "${CMAKE_CURRENT_BINARY_DIR}")
elseif(QNX)

View File

@ -15,6 +15,13 @@
# INSTALL_VERSIONED_LINK
# Prefix build only. On installation, create a versioned hard-link of the installed file.
# E.g. create a link of "bin/qmake6" to "bin/qmake".
# TRY_RUN
# On Windows, it creates a helper batch script that tests whether the tool can be executed
# successfully or not. If not, build halts and an error will be show, with tips on what
# might be cause, and how to fix it. TRY_RUN is disabled when cross-compiling.
# TRY_RUN_FLAGS
# Command line flags that are going to be passed to the tool for testing its correctness.
# If no flags were given, we default to `-v`.
#
# One-value Arguments:
# EXTRA_CMAKE_FILES
@ -42,11 +49,13 @@ function(qt_internal_add_tool target_name)
USER_FACING
INSTALL_VERSIONED_LINK
EXCEPTIONS
NO_UNITY_BUILD)
NO_UNITY_BUILD
TRY_RUN)
set(one_value_keywords
TOOLS_TARGET
INSTALL_DIR
CORE_LIBRARY
TRY_RUN_FLAGS
${__default_target_info_args})
set(multi_value_keywords
EXTRA_CMAKE_FILES
@ -105,6 +114,7 @@ function(qt_internal_add_tool target_name)
NO_INSTALL
${arg_NO_UNITY_BUILD}
SOURCES ${arg_SOURCES}
NO_PCH_SOURCES ${arg_NO_PCH_SOURCES}
NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES}
INCLUDE_DIRECTORIES
${arg_INCLUDE_DIRECTORIES}
@ -224,10 +234,62 @@ function(qt_internal_add_tool target_name)
qt_internal_apply_staging_prefix_build_rpath_workaround()
endif()
if(arg_TRY_RUN AND WIN32 AND NOT CMAKE_CROSSCOMPILING)
if(NOT arg_TRY_RUN_FLAGS)
set(arg_TRY_RUN_FLAGS "-v")
endif()
_qt_internal_add_try_run_post_build("${target_name}" "${arg_TRY_RUN_FLAGS}")
endif()
qt_enable_separate_debug_info(${target_name} "${install_dir}" QT_EXECUTABLE)
qt_internal_install_pdb_files(${target_name} "${install_dir}")
endfunction()
function(_qt_internal_add_try_run_post_build target try_run_flags)
qt_internal_get_upper_case_main_cmake_configuration(main_cmake_configuration)
get_target_property(target_out_dir ${target}
RUNTIME_OUTPUT_DIRECTORY_${main_cmake_configuration})
get_target_property(target_bin_dir ${target}
BINARY_DIR)
set(try_run_scripts_path "${target_bin_dir}/${target}_try_run.bat")
# The only reason -h is passed is because some of the tools, e.g., moc
# wait for an input without any arguments.
qt_configure_file(OUTPUT "${try_run_scripts_path}"
CONTENT "@echo off
${target_out_dir}/${target}.exe ${try_run_flags} > nul 2>&1
if \"%errorlevel%\" == \"-1073741515\" (
echo
echo '${target}' is built successfully, but some of the libraries
echo necessary for running it are missing. If you are building Qt with
echo 3rdparty libraries, make sure that you add their directory to the
echo PATH environment variable.
echo
exit /b %errorlevel%
)
echo. > ${target_bin_dir}/${target}_try_run_passed"
)
add_custom_command(
OUTPUT
${target_bin_dir}/${target}_try_run_passed
DEPENDS
${target}
COMMAND
${CMAKE_COMMAND} -E env QT_COMMAND_LINE_PARSER_NO_GUI_MESSAGE_BOXES=1
${try_run_scripts_path}
COMMENT
"Testing ${target} by trying to run it."
VERBATIM
)
add_custom_target(${target}_try_run ALL
DEPENDS ${target_bin_dir}/${target}_try_run_passed)
endfunction()
function(qt_export_tools module_name)
# Bail out when not building tools.
if(NOT QT_WILL_BUILD_TOOLS)
@ -287,7 +349,7 @@ function(qt_export_tools module_name)
string(REGEX REPLACE "_native$" "" tool_name ${tool_name})
endif()
set(extra_cmake_statements "${extra_cmake_statements}
if (NOT QT_NO_CREATE_TARGETS)
if(NOT QT_NO_CREATE_TARGETS AND ${INSTALL_CMAKE_NAMESPACE}${target}_FOUND)
__qt_internal_promote_target_to_global(${INSTALL_CMAKE_NAMESPACE}::${tool_name})
endif()
")

View File

@ -31,6 +31,14 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file}
list(APPEND init_platform "set(CMAKE_SYSTEM_PROCESSOR arm64 CACHE STRING \"\")")
endif()
if(QT_QMAKE_TARGET_MKSPEC)
list(APPEND init_platform
"if(NOT QT_QMAKE_TARGET_MKSPEC)"
" set(QT_QMAKE_TARGET_MKSPEC ${QT_QMAKE_TARGET_MKSPEC} CACHE STRING \"\")"
"endif()"
)
endif()
if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "linux-g++-32" AND NOT QT_NO_AUTO_DETECT_LINUX_X86)
set(__qt_toolchain_common_flags_init "-m32")
@ -121,14 +129,14 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file}
endif()
if(__qt_embed_toolchain_compilers)
list(APPEND init_platform "
set(__qt_initial_c_compiler \"${CMAKE_C_COMPILER}\")
set(__qt_initial_cxx_compiler \"${CMAKE_CXX_COMPILER}\")
if(NOT DEFINED CMAKE_C_COMPILER AND EXISTS \"\${__qt_initial_c_compiler}\")
set(CMAKE_C_COMPILER \"\${__qt_initial_c_compiler}\" CACHE STRING \"\")
endif()
if(NOT DEFINED CMAKE_CXX_COMPILER AND EXISTS \"\${__qt_initial_cxx_compiler}\")
set(CMAKE_CXX_COMPILER \"\${__qt_initial_cxx_compiler}\" CACHE STRING \"\")
endif()")
set(__qt_initial_c_compiler \"${CMAKE_C_COMPILER}\")
set(__qt_initial_cxx_compiler \"${CMAKE_CXX_COMPILER}\")
if(NOT DEFINED CMAKE_C_COMPILER AND EXISTS \"\${__qt_initial_c_compiler}\")
set(CMAKE_C_COMPILER \"\${__qt_initial_c_compiler}\" CACHE STRING \"\")
endif()
if(NOT DEFINED CMAKE_CXX_COMPILER AND EXISTS \"\${__qt_initial_cxx_compiler}\")
set(CMAKE_CXX_COMPILER \"\${__qt_initial_cxx_compiler}\" CACHE STRING \"\")
endif()")
endif()
unset(init_additional_used_variables)

View File

@ -6,17 +6,74 @@
# This is used for writing the config.opt file.
#
# This script takes the following arguments:
# IN_FILE: The input file. The whole command line as one string.
# IN_FILE: The input file. The whole command line as one string, or one argument per line.
# REDO_FILE: A file containing extra commands to be joined with IN_FILE.
# OUT_FILE: The output file. One argument per line.
# SKIP_ARGS: Number of arguments to skip from the front of the arguments list.
# IGNORE_ARGS: List of arguments to be ignored, i.e. that are not written.
#
# If the REDO_FILE is given, its parameters will be merged with IN_FILE parameters
# and be written into the OUT_FILE.
cmake_minimum_required(VERSION 3.16)
# Read arguments from IN_FILE and separate them.
file(READ "${IN_FILE}" raw_args)
# To catch cases where the path ends with an `\`, e.g., `-prefix "C:\Path\"`
string(REPLACE "\\\"" "\"" raw_args "${raw_args}")
string(REPLACE ";" "[[;]]" raw_args "${raw_args}")
separate_arguments(args NATIVE_COMMAND "${raw_args}")
string(REPLACE "\;" ";" args "${args}")
string(REPLACE "[[;]]" "\;" args "${args}")
if(DEFINED REDO_FILE)
file(READ "${REDO_FILE}" raw_redo_args)
separate_arguments(redo_args NATIVE_COMMAND "${raw_redo_args}")
if(args)
list(FIND args "--" args_ddash_loc)
list(FIND redo_args "--" redo_ddash_loc)
if("${redo_ddash_loc}" STREQUAL "-1")
if("${args_ddash_loc}" STREQUAL "-1")
list(LENGTH args args_ddash_loc)
endif()
# Avoid adding an empty line for an empty -redo
if(NOT "${redo_args}" STREQUAL "")
list(INSERT args ${args_ddash_loc} "${redo_args}")
endif()
else()
# Handling redo's configure options
list(SUBLIST redo_args 0 ${redo_ddash_loc} redo_config_args)
if(redo_config_args)
if("${args_ddash_loc}" STREQUAL "-1")
list(APPEND args "${redo_config_args}")
else()
list(INSERT args ${args_ddash_loc} "${redo_config_args}")
endif()
endif()
# Handling redo's CMake options
list(LENGTH redo_args redo_args_len)
math(EXPR redo_ddash_loc "${redo_ddash_loc} + 1")
# Catch an unlikely case of -redo being called with an empty --, ie., `-redo --`
if(NOT ${redo_ddash_loc} STREQUAL ${redo_args_len})
list(SUBLIST redo_args ${redo_ddash_loc} -1 redo_cmake_args)
endif()
if(DEFINED redo_cmake_args)
if("${args_ddash_loc}" STREQUAL "-1")
list(APPEND args "--")
endif()
list(APPEND args "${redo_cmake_args}")
endif()
endif()
else()
list(APPEND args "${redo_args}")
endif()
endif()
# Skip arguments if requested
if(DEFINED SKIP_ARGS)
foreach(i RANGE 1 ${SKIP_ARGS})

View File

@ -17,22 +17,22 @@
#endif
#if !defined(QT_BUILD_@module_define_infix@_LIB) && !defined(QT_STATIC)
/* outside library inline decl + defi */
/* outside library -> inline decl + defi */
/* static builds treat everything as part of the library, so they never inline */
# define QT_@module_define_infix@_INLINE_SINCE(major, minor) inline
# define QT_@module_define_infix@_INLINE_IMPL_SINCE(major, minor) 1
#elif defined(QT_@module_define_infix@_BUILD_REMOVED_API)
/* inside library, inside removed_api.cpp:
* keep deprecated API non-inline decl;
* remove deprecated API inline decl;
* keep deprecated API -> non-inline decl;
* remove deprecated API -> inline decl;
* definition is always available */
# define QT_@module_define_infix@_INLINE_SINCE(major, minor) \
QT_IF_DEPRECATED_SINCE(major, minor, inline, /* not inline */)
# define QT_@module_define_infix@_INLINE_IMPL_SINCE(major, minor) 1
#else
/* inside library, outside removed_api.cpp:
* keep deprecated API non-inline decl, no defi;
* remove deprecated API inline decl, defi */
* keep deprecated API -> non-inline decl, no defi;
* remove deprecated API -> inline decl, defi */
# define QT_@module_define_infix@_INLINE_SINCE(major, minor) \
QT_IF_DEPRECATED_SINCE(major, minor, inline, /* not inline */)
# define QT_@module_define_infix@_INLINE_IMPL_SINCE(major, minor) \

View File

@ -13,6 +13,11 @@ instructions:
variableValue: "{{.Env.COMMON_TEST_CMAKE_ARGS}}"
- !include "{{qt/qtbase}}/cmake_build_and_upload_test_artifacts.yaml"
disable_if:
condition: property
property: features
contains_value: DisableTests
condition: or
conditions:
- condition: property
property: features
contains_value: DisableTests
- condition: property
property: features
contains_value: DoNotBuildTests

View File

@ -34,6 +34,11 @@ instructions:
variableValue: "{{.Env.COMMON_TARGET_TEST_CMAKE_ARGS}}"
- !include "{{qt/qtbase}}/cmake_build_and_upload_test_artifacts.yaml"
disable_if:
condition: property
property: features
contains_value: DisableTests
condition: or
conditions:
- condition: property
property: features
contains_value: DisableTests
- condition: property
property: features
contains_value: DoNotBuildTests

View File

@ -61,9 +61,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- type: EnvironmentVariable
variableName: COIN_CMAKE_INSTALL_LIBEXEC_DIR
variableValue: "{{.InstallDir}}{{.Env.CI_PATH_SEP}}host{{.Env.CI_PATH_SEP}}bin"
@ -73,9 +78,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- !include "{{qt/qtbase}}/call_host_install.yaml"
- type: EnvironmentVariable
variableName: DESTDIR
@ -123,9 +133,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- type: EnvironmentVariable
variableName: COIN_CMAKE_INSTALL_LIBEXEC_DIR
variableValue: "{{.InstallDir}}{{.Env.CI_PATH_SEP}}target{{.Env.CI_PATH_SEP}}bin"
@ -135,9 +150,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- !include "{{qt/qtbase}}/call_target_install.yaml"
- type: EnvironmentVariable
variableName: DESTDIR

View File

@ -47,9 +47,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- type: EnvironmentVariable
variableName: COIN_CMAKE_BUILD_LIBEXEC_DIR
variableValue: "{{.BuildDir}}{{.Env.CI_PATH_SEP}}bin"
@ -59,9 +64,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- !include "{{qt/qtbase}}/call_host_install.yaml"
- type: EnvironmentVariable
variableName: DESTDIR
@ -124,9 +134,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- type: EnvironmentVariable
variableName: COIN_CMAKE_BUILD_LIBEXEC_DIR
variableValue: "{{.Env.COIN_CMAKE_BUILD_DIR}}{{.Env.CI_PATH_SEP}}bin"
@ -136,9 +151,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- !include "{{qt/qtbase}}/call_target_install.yaml"
- type: EnvironmentVariable
variableName: DESTDIR

View File

@ -41,9 +41,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- type: EnvironmentVariable
variableName: COIN_CMAKE_BUILD_LIBEXEC_DIR
variableValue: "{{.Env.COIN_CMAKE_BUILD_DIR}}{{.Env.CI_PATH_SEP}}bin"
@ -53,9 +58,14 @@ instructions:
- condition: property
property: host.os
equals_value: Windows
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- condition: and
conditions:
- condition: property
property: target.os
not_equals_value: QNX
- condition: property
property: target.osVersion
not_in_values: [WebAssembly, Android_ANY]
- !include "{{qt/qtbase}}/call_host_install.yaml"
- type: EnvironmentVariable
variableName: DESTDIR

View File

@ -305,6 +305,14 @@ instructions:
condition: property
property: target.osVersion
in_values: [QEMU]
# This fixes an issue where binfmts is sometimes disabled on the test VMs
- type: ExecuteCommand
command: sudo update-binfmts --enable
userMessageOnFailure: "Failed to enable binfmts"
enable_if:
condition: property
property: target.osVersion
equals_value: QEMU
# Windows on Arm, cross-compilation with MSVC
- type: Group

View File

@ -1,300 +0,0 @@
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from conans import ConanFile, tools
from conans.errors import ConanInvalidConfiguration
import os
import re
import shutil
from functools import lru_cache
from pathlib import Path
from typing import Dict, Union
class QtConanError(Exception):
pass
def add_cmake_prefix_path(conan_file: ConanFile, dep: str) -> None:
if dep not in conan_file.deps_cpp_info.deps:
raise QtConanError("Unable to find dependency: {0}".format(dep))
dep_cpp_info = conan_file.deps_cpp_info[dep]
cmake_args_str = str(conan_file.options.get_safe("cmake_args_qtbase", default=""))
formatted_cmake_args_str = conan_file._shared.append_cmake_arg(
cmake_args_str, "CMAKE_PREFIX_PATH", dep_cpp_info.rootpath
)
print("Adjusted cmake args for qtbase build: {0}".format(formatted_cmake_args_str))
setattr(conan_file.options, "cmake_args_qtbase", formatted_cmake_args_str)
def _build_qtbase(conan_file: ConanFile):
# we call the Qt's configure(.bat) directly
script = Path("configure.bat") if tools.os_info.is_windows else Path("configure")
configure = Path(conan_file.build_folder).joinpath(script).resolve(strict=True)
if conan_file.options.get_safe("icu", default=False):
# we need to tell Qt build system where to find the ICU
add_cmake_prefix_path(conan_file, dep="icu")
# convert the Conan options to Qt configure(.bat) arguments
parser = conan_file._qt_option_parser
qt_configure_options = parser.convert_conan_options_to_qt_options(conan_file.options)
cmd = " ".join(
[str(configure), " ".join(qt_configure_options), "-prefix", conan_file.package_folder]
)
cmake_args = parser.get_cmake_args_for_configure(conan_file.options)
if cmake_args:
cmd += " -- {0}".format(" ".join(cmake_args))
conan_file.output.info("Calling: {0}".format(cmd))
conan_file.run(cmd)
cmd = " ".join(["cmake", "--build", ".", "--parallel"])
conan_file.output.info("Calling: {0}".format(cmd))
conan_file.run(cmd)
@lru_cache(maxsize=8)
def _parse_qt_version_by_key(key: str) -> str:
with open(Path(__file__).parent.resolve() / ".cmake.conf") as f:
m = re.search(fr'{key} .*"(.*)"', f.read())
return m.group(1) if m else ""
def _get_qt_minor_version() -> str:
return ".".join(_parse_qt_version_by_key("QT_REPO_MODULE_VERSION").split(".")[:2])
class QtBase(ConanFile):
name = "qtbase"
license = "LGPL-3.0, GPL-2.0+, Commercial Qt License Agreement"
author = "The Qt Company <https://www.qt.io/contact-us>"
url = "https://code.qt.io/cgit/qt/qtbase.git"
description = "Qt6 core framework libraries and tools."
topics = ("qt", "qt6")
settings = "os", "compiler", "arch", "build_type"
_qt_option_parser = None
options = None
default_options = None
exports_sources = "*", "!conan*.*"
# use commit ID as the RREV (recipe revision)
revision_mode = "scm"
python_requires = "qt-conan-common/{0}@qt/everywhere".format(_get_qt_minor_version())
short_paths = True
_shared = None
def init(self):
self._shared = self.python_requires["qt-conan-common"].module
self._qt_option_parser = self._shared.QtOptionParser(Path(__file__).parent.resolve())
self.options = self._qt_option_parser.get_qt_conan_options()
self.default_options = self._qt_option_parser.get_default_qt_conan_options()
def set_version(self):
# Executed during "conan export" i.e. in source tree
_ver = _parse_qt_version_by_key("QT_REPO_MODULE_VERSION")
_prerelease = _parse_qt_version_by_key("QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT")
self.version = _ver + "-" + _prerelease if _prerelease else _ver
def export(self):
self.copy("configure_options.json")
self.copy("configure_features.txt")
self.copy(".cmake.conf")
conf = self._shared.qt_sw_versions_config_folder() / self._shared.qt_sw_versions_config_name()
if not conf.exists():
# If using "conan export" outside Qt CI provisioned machines
print("Warning: Couldn't find '{0}'. 3rd party dependencies skipped.".format(conf))
else:
shutil.copy2(conf, self.export_folder)
def requirements(self):
# list of tuples, (package_name, fallback version)
optional_requirements = [("icu", "56.1")]
for req_name, req_ver_fallback in optional_requirements:
if self.options.get_safe(req_name, default=False) == True:
# Note! If this conan package is being "conan export"ed outside Qt CI and the
# sw versions .ini file is not present then it will fall-back to default version
ver = self._shared.parse_qt_sw_pkg_dependency(
config_folder=Path(self.recipe_folder),
package_name=req_name,
target_os=str(self.settings.os),
)
if not ver:
print(
"Warning: Using fallback version '{0}' for: {1}".format(
req_name, req_ver_fallback
)
)
ver = req_ver_fallback
requirement = "{0}/{1}@qt/everywhere".format(req_name, ver)
print("Setting 3rd party package requirement: {0}".format(requirement))
self.requires(requirement)
def _resolve_qt_host_path(self) -> Union[str, None]:
"""
Attempt to resolve QT_HOST_PATH.
When cross-building the user needs to pass 'qt_host_path' which is transformed to
QT_HOST_PATH later on. Resolve the exact path if possible.
Returns:
string: The resolved QT_HOST_PATH or None if unable to determine it.
"""
_host_p = self.options.get_safe("qt_host_path")
if _host_p:
return str(Path(os.path.expandvars(str(_host_p))).expanduser().resolve(strict=True))
else:
print("Warning: 'qt_host_path' option was not given in cross-build context")
return None
def configure(self):
if self.settings.compiler == "gcc" and tools.Version(self.settings.compiler.version) < "8":
raise ConanInvalidConfiguration("Qt6 does not support GCC before 8")
def _set_default_if_not_set(option_name: str, option_value: bool) -> None:
# let it fail if option name does not exist, it means the recipe is not up to date
if self.options.get_safe(option_name) in [None, "None"]:
setattr(self.options, option_name, option_value)
def _set_build_type(build_type: str) -> None:
if self.settings.build_type != build_type:
msg = (
"The build_type '{0}' changed to '{1}'. Please check your Settings and "
"Options. The used Qt options enforce '{2}' as a build_type. ".format(
self.settings.build_type, build_type, build_type
)
)
raise QtConanError(msg)
self.settings.build_type = build_type
def _check_mutually_exclusive_options(options: Dict[str, bool]) -> None:
if list(options.values()).count(True) > 1:
raise QtConanError(
"These Qt options are mutually exclusive: {0}"
". Choose only one of them and try again.".format(list(options.keys()))
)
default_options = ["shared", "gui", "widgets", "accessibility", "system_proxies", "ico"]
if self.settings.os == "Macos":
default_options.append("framework")
for item in default_options:
_set_default_if_not_set(item, True)
release = self.options.get_safe("release", default=False)
debug = self.options.get_safe("debug", default=False)
debug_and_release = self.options.get_safe("debug_and_release", default=False)
force_debug_info = self.options.get_safe("force_debug_info", default=False)
optimize_size = self.options.get_safe("optimize_size", default=False)
# these options are mutually exclusive options so do a sanity check
_check_mutually_exclusive_options(
{"release": release, "debug": debug, "debug_and_release": debug_and_release}
)
# Prioritize Qt's configure options over Settings.build_type
if debug_and_release == True:
# Qt build system will build both debug and release binaries
if force_debug_info == True:
_set_build_type("RelWithDebInfo")
else:
_set_build_type("Release")
elif release == True:
_check_mutually_exclusive_options(
{"force_debug_info": force_debug_info, "optimize_size": optimize_size}
)
if force_debug_info == True:
_set_build_type("RelWithDebInfo")
elif optimize_size == True:
_set_build_type("MinSizeRel")
else:
_set_build_type("Release")
elif debug == True:
_set_build_type("Debug")
else:
# As a fallback set the build type for Qt configure based on the 'build_type'
# defined in the conan build settings
build_type = self.settings.get_safe("build_type")
if build_type in [None, "None"]:
# set default that mirror the configure(.bat) default values
self.options.release = True
self.settings.build_type = "Release"
elif build_type == "Release":
self.options.release = True
elif build_type == "Debug":
self.options.debug = True
elif build_type == "RelWithDebInfo":
self.options.release = True
self.options.force_debug_info = True
elif build_type == "MinSizeRel":
self.options.release = True
self.options.optimize_size = True
else:
raise QtConanError("Unknown build_type: {0}".format(self.settings.build_type))
if self.settings.os == "Android":
if self.options.get_safe("android_sdk_version") == None:
cmake_args_qtbase = str(self.options.get_safe("cmake_args_qtbase"))
sdk_ver = self._shared.parse_android_sdk_version(cmake_args_qtbase)
if sdk_ver:
print("'android_sdk_version' not given. Deduced version: {0}".format(sdk_ver))
self.options.android_sdk_version = sdk_ver
else:
# TODO, for now we have no clean means to query the Android SDK version from
# Qt build system so we just exclude the "android_sdk" from the package_id.
print("Can't deduce 'android_sdk_version'. Excluding it from 'package_id'")
delattr(self.info.options, "android_sdk_version")
if self.options.get_safe("android_ndk_version") == None:
ndk_ver = str(self.options.get_safe("android_ndk"))
ndk_ver = self._shared.parse_android_ndk_version(Path(ndk_ver, strict=True))
print("'android_ndk_version' not given. Deduced version: {0}".format(ndk_ver))
self.options.android_ndk_version = ndk_ver
def build(self):
self._shared.build_env_wrap(self, _build_qtbase)
def package(self):
self._shared.call_install(self)
def package_info(self):
self._shared.package_info(self)
if tools.cross_building(conanfile=self):
qt_host_path = self._resolve_qt_host_path()
if qt_host_path:
self.env_info.QT_HOST_PATH.append(qt_host_path)
def package_id(self):
# https://docs.conan.io/en/latest/creating_packages/define_abi_compatibility.html
# The package_revision_mode() is too strict for Qt CI. This mode includes artifacts
# checksum in package_id which is problematic in Qt CI re-runs (re-run flaky
# build) which contain different build timestamps (cmake) which end up in library
# files -> different package_id.
self.info.requires.recipe_revision_mode()
# Enable 'qt-conan-common' updates on client side with $conan install .. --update
self.info.python_requires.recipe_revision_mode()
# Remove those configure(.bat) options which should not affect package_id.
# These point to local file system paths and in order to re-use pre-built
# binaries (by Qt CI) by others these should not affect the 'package_id'
# as those probably differ on each machine
rm_list = [
"sdk",
"qpa",
"translationsdir",
"headersclean",
"qt_host_path",
"android_sdk",
"android_ndk",
]
for item in rm_list:
if item in self.info.options:
delattr(self.info.options, item)
# filter also those cmake options that should not end up in the package_id
if hasattr(self.info.options, "cmake_args_qtbase"):
_filter = self._shared.filter_cmake_args_for_package_id
self.info.options.cmake_args_qtbase = _filter(self.info.options.cmake_args_qtbase)
def deploy(self):
self.copy("*") # copy from current package
self.copy_deps("*") # copy from dependencies

144
configure vendored
View File

@ -8,14 +8,18 @@
#-------------------------------------------------------------------------------
# the directory of this script is the "source tree"
relpath=`dirname $0`
relpath=`dirname "$0"`
relpath=`(cd "$relpath"; /bin/pwd)`
# the current directory is the "build tree" or "object tree"
outpath=`/bin/pwd`
outpathPrefix=$outpath
# do this early so we don't store it in config.status
CFG_TOPLEVEL=
outpathPrefix=
SAVED_IFS=$IFS
IFS='
'
checkTopLevelBuild()
{
@ -23,7 +27,7 @@ checkTopLevelBuild()
if [ x"$1" = x"-top-level" ]; then
CFG_TOPLEVEL=yes
relpathMangled=`dirname "$relpath"`
outpathPrefix=../
outpathPrefix="$outpathPrefix/.."
else
if [ -f ../.qmake.super ]; then
echo >&2 "ERROR: You cannot configure qtbase separately within a top-level build."
@ -35,41 +39,26 @@ checkTopLevelBuild()
OPT_CMDLINE= # expanded version for the script
determineOptFilePath()
{
> "${outpathPrefix}/config.redo.in"
set -f # suppress globbing in for loop
SAVED_IFS=$IFS
IFS='
'
for i in "$@"; do
if [ x"$i" = x"-top-level" ]; then
continue
fi
case $i in
-redo|--redo)
optfile=${outpathPrefix}config.opt
if test -n "$CFG_TOPLEVEL" && ! test -f $optfile; then
optfile=config.opt
fi
if ! test -f $optfile; then
optfile=${outpathPrefix}/config.opt
if ! test -f "$optfile"; then
echo >&2 "No config.opt present - cannot redo configuration."
exit 1
fi
for a in `cat $optfile`; do
OPT_CMDLINE="$OPT_CMDLINE
$a"
done
;;
*)
OPT_CMDLINE="$OPT_CMDLINE
$i"
# If redo-ing, write the rest of parameters into the config.redo.in file
echo \"$i\" >> "${outpathPrefix}/config.redo.in"
;;
esac
done
set --
for i in $OPT_CMDLINE; do
set -- "$@" "$i"
done
set +f
IFS=$SAVED_IFS
}
#-------------------------------------------------------------------------------
@ -88,80 +77,10 @@ while [ "$#" -gt 0 ]; do
CURRENT_OPT="$1"
case "$1" in
#Autoconf style options
--enable-*)
VAR=`echo $1 | sed 's,^--enable-\(.*\),\1,'`
VAL=yes
;;
--disable-*)
VAR=`echo $1 | sed 's,^--disable-\(.*\),\1,'`
VAL=no
;;
--*=*)
VAR=`echo $1 | sed 's,^--\(.*\)=.*,\1,'`
VAL=`echo $1 | sed 's,^--.*=\(.*\),\1,'`
;;
--no-*)
VAR=`echo $1 | sed 's,^--no-\(.*\),\1,'`
VAL=no
;;
--*)
VAR=`echo $1 | sed 's,^--\(.*\),\1,'`
VAL=yes
;;
#Qt plugin options
-no-*-*|-plugin-*-*|-qt-*-*)
VAR=`echo $1 | sed 's,^-[^-]*-\(.*\),\1,'`
VAL=`echo $1 | sed 's,^-\([^-]*\).*,\1,'`
;;
#Qt style no options
-no-*)
VAR=`echo $1 | sed 's,^-no-\(.*\),\1,'`
VAL=no
;;
#Qt style options that pass an argument
-prefix| \
-docdir| \
-headerdir| \
-plugindir| \
-qmldir| \
-archdatadir| \
-datadir| \
-libdir| \
-bindir| \
-libexecdir| \
-translationdir| \
-sysconfdir| \
-examplesdir| \
-testsdir| \
-hostdatadir| \
-extprefix| \
-sysroot| \
-make| \
-nomake| \
-skip| \
-platform| \
-xplatform| \
-device| \
-device-option| \
-sdk| \
-android-sdk| \
-android-ndk| \
-android-ndk-platform| \
-android-arch)
VAR=`echo $1 | sed 's,^-\(.*\),\1,'`
shift
VAL="$1"
;;
#Qt style complex options in one command
-enable-*|-disable-*)
VAR=`echo $1 | sed 's,^-\([^-]*\)-.*,\1,'`
VAL=`echo $1 | sed 's,^-[^-]*-\(.*\),\1,'`
;;
#Qt Builtin/System style options
-no-*|-system-*|-qt-*)
VAR=`echo $1 | sed 's,^-[^-]*-\(.*\),\1,'`
VAL=`echo $1 | sed 's,^-\([^-]*\)-.*,\1,'`
;;
#General options, including Qt style yes options
-*)
VAR=`echo $1 | sed 's,^-\(.*\),\1,'`
@ -176,24 +95,16 @@ while [ "$#" -gt 0 ]; do
shift
UNKNOWN_OPT=no
case "$VAR" in
h|help)
if [ "$VAL" = "yes" ]; then
OPT_HELP="$VAL"
else
UNKNOWN_OPT=yes
fi
;;
*)
;;
esac
if [ "$UNKNOWN_OPT" = "yes" ]; then
echo "${CURRENT_OPT}: invalid command-line switch"
ERROR=yes
fi
done
[ "x$ERROR" = "xyes" ] && exit 1
}
#-------------------------------------------------------------------------------
@ -222,18 +133,27 @@ parseCommandline "$@"
handleHelp
determineOptFilePath "$@"
optfilepath=${outpathPrefix}/config.opt
opttmpfilepath=${outpathPrefix}/config.opt.in
redofilepath=${outpathPrefix}/config.redo
redotmpfilepath=${outpathPrefix}/config.redo.in
fresh_requested_arg=
optfilename=config.opt
if [ -z "$optfile" ]; then # only write optfile if not currently redoing
optfilepath=${outpathPrefix}${optfilename}
> "$optfilepath"
for arg in "$@"; do
if [ "$arg" = "-top-level" ]; then
continue
fi
echo $arg >> "$optfilepath"
done
> "$opttmpfilepath"
> "$redotmpfilepath"
for arg in "$@"; do echo \"$arg\" >> "$opttmpfilepath"; done
cmake -DIN_FILE="${opttmpfilepath}" -DOUT_FILE="${optfilepath}" -DIGNORE_ARGS=-top-level -P "${relpath}/cmake/QtWriteArgsFile.cmake"
else
# Rewriting config.opt into config.opt.in anyway. Allows for direct manipulation of config.opt
> "$opttmpfilepath"
for arg in `cat $optfile`; do echo \"$arg\" >> "$opttmpfilepath"; done
cmake -DIN_FILE="${opttmpfilepath}" -DREDO_FILE="${redotmpfilepath}" -DOUT_FILE="${redofilepath}" -DIGNORE_ARGS=-top-level -P "${relpath}/cmake/QtWriteArgsFile.cmake"
optfilepath=${redofilepath}
fresh_requested_arg=-DFRESH_REQUESTED=TRUE
fi
@ -243,4 +163,6 @@ if [ -n "$CFG_TOPLEVEL" ]; then
cd ..
fi
cmake "-DOPTFILE=$optfilename" $top_level_arg $fresh_requested_arg -P "$relpath/cmake/QtProcessConfigureArgs.cmake"
cmake "-DOPTFILE=${optfilepath}" ${top_level_arg} ${fresh_requested_arg} -P "${relpath}/cmake/QtProcessConfigureArgs.cmake"
IFS=$SAVED_IFS

View File

@ -65,9 +65,8 @@ goto doneargs
:redo
if not exist "%TOPQTDIR%\config.opt" goto redoerr
set rargs=
for /f "usebackq delims=" %%i in ("%TOPQTDIR%\config.opt") do set rargs=!rargs! "%%i"
call :doargs %rargs%
echo %ARGS% > %TOPQTDIR%\config.redo.in
set redoing=""
goto nextarg
:redoerr
echo No config.opt present - cannot redo configuration. >&2
@ -78,15 +77,26 @@ goto doneargs
cd "%TOPQTDIR%"
rem Write config.opt if we're not currently -redo'ing
set OPT_FILE_PATH=%TOPQTDIR%\config.opt
set OPT_TMP_FILE_PATH=%TOPQTDIR%\config.opt.in
set REDO_FILE_PATH=%TOPQTDIR%\config.redo
set REDO_TMP_FILE_PATH=%TOPQTDIR%\config.redo.in
set FRESH_REQUESTED_ARG=
if "!rargs!" == "" (
echo.%*>config.opt.in
cmake -DIN_FILE=config.opt.in -DOUT_FILE=config.opt -DIGNORE_ARGS=-top-level -P "%QTSRC%\cmake\QtWriteArgsFile.cmake"
) else if NOT "!rargs!" == "" (
if not defined redoing (
echo.%*>"%OPT_TMP_FILE_PATH%"
cmake -DIN_FILE="%OPT_TMP_FILE_PATH%" -DOUT_FILE="%OPT_FILE_PATH%" -DIGNORE_ARGS=-top-level -P "%QTSRC%\cmake\QtWriteArgsFile.cmake"
) else (
echo. 2> "%OPT_TMP_FILE_PATH%"
for /F "usebackq tokens=*" %%A in ("%OPT_FILE_PATH%") do echo "%%A" >> "%OPT_TMP_FILE_PATH%"
cmake -DIN_FILE="%OPT_TMP_FILE_PATH%" -DREDO_FILE="%REDO_TMP_FILE_PATH%" -DOUT_FILE="%REDO_FILE_PATH%" -DIGNORE_ARGS="-top-level;-redo;--redo" -P "%QTSRC%\cmake\QtWriteArgsFile.cmake"
set OPT_FILE_PATH=%REDO_FILE_PATH%
set FRESH_REQUESTED_ARG=-DFRESH_REQUESTED=TRUE
)
rem Launch CMake-based configure
set TOP_LEVEL_ARG=
if %TOPLEVEL% == true set TOP_LEVEL_ARG=-DTOP_LEVEL=TRUE
cmake -DOPTFILE=config.opt %TOP_LEVEL_ARG% %FRESH_REQUESTED_ARG% -P "%QTSRC%\cmake\QtProcessConfigureArgs.cmake"
cmake -DOPTFILE="%OPT_FILE_PATH%" %TOP_LEVEL_ARG% %FRESH_REQUESTED_ARG% -P "%QTSRC%\cmake\QtProcessConfigureArgs.cmake"

View File

@ -22,6 +22,7 @@ qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpen
# openssl_headers
# OPENSSL_VERSION_MAJOR is not defined for OpenSSL 1.1.1
qt_config_compile_test(opensslv11_headers
LABEL "opensslv11_headers"
LIBRARIES
WrapOpenSSLHeaders::WrapOpenSSLHeaders
CODE
@ -46,6 +47,7 @@ qt_find_package(WrapOpenSSL PROVIDED_TARGETS WrapOpenSSL::WrapOpenSSL MODULE_NAM
# openssl
# OPENSSL_VERSION_MAJOR is not defined for OpenSSL 1.1.1
qt_config_compile_test(opensslv11
LABEL "opensslv11"
LIBRARIES
WrapOpenSSL::WrapOpenSSL
CODE
@ -70,6 +72,7 @@ SSL_free(SSL_new(0));
# opensslv30
# openssl_headers
qt_config_compile_test(opensslv30_headers
LABEL "opensslv30_headers"
LIBRARIES
WrapOpenSSLHeaders::WrapOpenSSLHeaders
CODE
@ -87,6 +90,7 @@ int main(void)
}
")
qt_config_compile_test(opensslv30
LABEL "opensslv30"
LIBRARIES
WrapOpenSSL::WrapOpenSSL
CODE
@ -331,11 +335,13 @@ int main(void)
"# FIXME: qmake: ['TEMPLATE = lib', 'CONFIG += dll bsymbolic_functions', 'isEmpty(QMAKE_LFLAGS_BSYMBOLIC_FUNC): error("Nope")']
)
if(NOT MSVC AND NOT APPLE)
qt_config_compile_test("separate_debug_info"
LABEL "separate debug information support"
PROJECT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/config.tests/separate_debug_info"
)
endif()
qt_config_compile_test("separate_debug_info"
LABEL "separate debug information support"
PROJECT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/config.tests/separate_debug_info"
)
# signaling_nan
qt_config_compile_test(signaling_nan
LABEL "Signaling NaN for doubles"
@ -535,7 +541,7 @@ qt_feature("developer-build" PRIVATE
LABEL "Developer build"
AUTODETECT OFF
)
qt_feature("no-prefix" PRIVATE
qt_feature("no-prefix"
LABEL "No prefix build"
AUTODETECT NOT QT_WILL_INSTALL
CONDITION NOT QT_WILL_INSTALL
@ -567,7 +573,7 @@ qt_feature_config("force_debug_info" QMAKE_PRIVATE_CONFIG)
qt_feature("separate_debug_info" PUBLIC
LABEL "Split off debug information"
AUTODETECT OFF
CONDITION ( QT_FEATURE_shared ) AND ( QT_FEATURE_debug OR QT_FEATURE_debug_and_release OR QT_FEATURE_force_debug_info ) AND ( APPLE OR TEST_separate_debug_info )
CONDITION ( QT_FEATURE_shared ) AND ( QT_FEATURE_debug OR QT_FEATURE_debug_and_release OR QT_FEATURE_force_debug_info ) AND ( MSVC OR APPLE OR TEST_separate_debug_info )
)
qt_feature_config("separate_debug_info" QMAKE_PUBLIC_QT_CONFIG)
qt_feature("appstore-compliant" PUBLIC
@ -1157,6 +1163,18 @@ qt_configure_add_summary_entry(
ARGS "ccache"
CONDITION UNIX
)
qt_configure_add_summary_entry(
TYPE "message" ARGS "Unity Build" MESSAGE "yes" CONDITION QT_UNITY_BUILD
)
qt_configure_add_summary_entry(
TYPE "message" ARGS "Unity Build" MESSAGE "no" CONDITION NOT QT_UNITY_BUILD
)
qt_configure_add_summary_entry(
TYPE "message"
ARGS "Unity Build Batch Size"
MESSAGE "${QT_UNITY_BUILD_BATCH_SIZE}"
CONDITION QT_UNITY_BUILD
)
qt_configure_add_summary_entry(
TYPE "firstAvailableFeature"
ARGS "use_bfd_linker use_gold_linker use_lld_linker use_mold_linker"
@ -1326,3 +1344,11 @@ qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)
qt_extra_definition("QT_COPYRIGHT" \"${QT_COPYRIGHT}\" PRIVATE)
qt_extra_definition("QT_COPYRIGHT_YEAR" \"${QT_COPYRIGHT_YEAR}\" PRIVATE)
qt_configure_add_report_entry(
TYPE WARNING
MESSAGE "QT_ALLOW_SYMLINK_IN_PATHS is enabled. This is not recommended, and it may lead to unexpected issues.
E.g., When building QtWebEngine, enabling this option may result in build issues in certain platforms.
See https://bugreports.qt.io/browse/QTBUG-59769."
CONDITION QT_ALLOW_SYMLINK_IN_PATHS
)

View File

@ -0,0 +1 @@
url.examples = "https://code.qt.io/cgit/qt/qtpositioning.git/tree/examples/\1?h=$QT_VER"

View File

@ -29,6 +29,5 @@ url = https://doc.qt.io/qt
defines += qt6
# Uncomment the following two lines to generate documentation marked as \internal
# alias.internal = disable
# macro.internal.HTML = "<span style="color:red">[internal]</span>"
# Require Qt modules to define qhp.projects
qhp = true

View File

@ -2,11 +2,13 @@
#specify the CSS file used by this template
HTML.stylesheets = template/style/offline.css \
template/style/offline-dark.css
template/style/offline-dark.css \
template/style/tech_preview.svg
#for including files into the qch file
qhp.extraFiles += style/offline.css \
style/offline-dark.css
style/offline-dark.css \
style/tech_preview.svg
HTML.headerstyles = \
" <link rel=\"stylesheet\" type=\"text/css\" href=\"style/offline.css\" />\n"

View File

@ -11,8 +11,10 @@ HTML.stylesheets = template/style/online.css \
template/style/icomoon.woff \
template/style/cookiebar-x.png \
template/style/doc_search.png \
template/style/tech_preview.svg \
template/style/theqtcompany.png
#for including files into the qch file. Relative to the outputdir of a QDoc build.
qhp.extraFiles += style/online.css \
style/cookie-confirm.css \
@ -25,6 +27,7 @@ qhp.extraFiles += style/online.css \
style/icomoon.woff \
style/cookiebar-x.png \
style/doc_search.png \
style/tech_preview.svg \
style/theqtcompany.png
HTML.headerstyles = \

View File

@ -56,6 +56,19 @@ macro.endqdoc.HTML = "*/"
macro.borderedimage = "\\div {class=\"border\"} \\image \1\n\\enddiv"
macro.examplecategory = "\\meta category {\1}\n\\ingroup category \1"
macro.QDS = "Qt Design Studio"
macro.QDV = "Qt Design Viewer"
macro.QB = "Qt Bridge"
macro.QBPS = "Qt Bridge for Adobe Photoshop"
macro.QBXD = "Qt Bridge for Adobe XD"
macro.QBSK = "Qt Bridge for Sketch"
macro.QBF = "Qt Bridge for Figma"
macro.QMCU = "Qt for MCUs"
macro.QUL = "Qt Quick Ultralite"
macro.QtAA = "Qt for Android Automotive"
macro.QOI = "Qt Online Installer"
macro.QMT = "Qt Maintenance Tool"
macro.beginfloatleft.HTML = "<div style=\"float: left; margin-right: 2em\">"
macro.beginfloatright.HTML = "<div style=\"float: right; margin-left: 2em\">"
macro.endfloat.HTML = "</div>"
@ -111,3 +124,8 @@ macro.cmakepropertywebassemblyonly = "\\note This property is used only if targe
macro.cmakepropertyiosonly = "\\note This property is used only if targeting iOS."
macro.cmakevariableiosonly = "\\note This variable is used only if targeting iOS."
#Appends the tech preview link to the brief sentence and adds to tech_preview
#group.
#Must be placed directly under a \brief command
macro.techpreview = "(Technical preview)\n\n\\meta status {Technical preview}\n\\ingroup tech_preview\n"

View File

@ -94,7 +94,6 @@ manifestmeta.android.names = "Qt3D/Qt 3D: Basic Shapes C++ Example" \
"QtQuickControls/Qt Quick Controls - Flat Style" \
"QtQuickControls/Qt Quick Controls - Gallery" \
"QtQuickControls/Qt Quick Controls - Imagine Style Example: Automotive" \
"QtQuickControls/Qt Quick Controls - Imagine Style Example: Music Player" \
"QtQuickControls/Qt Quick Controls - Side Panel" \
"QtQuickControls/Qt Quick Controls - Swipe to Remove" \
"QtQuickControls/Qt Quick Controls - Text Editor" \
@ -166,7 +165,6 @@ manifestmeta.ios.names = "QtCore/Contiguous Cache Example" \
"QtWidgets/Easing Curves Example" \
"QtWidgets/Move Blocks Example" \
"QtWidgets/States Example" \
"QtWidgets/Class Wizard Example" \
"QtWidgets/Find Files Example" \
"QtWidgets/License Wizard Example" \
"QtWidgets/Standard Dialogs Example" \

View File

@ -574,6 +574,20 @@ ol.I > li {
padding: 3px 15px 3px 0
}
span.status.technical-preview {
display: inline-block;
position: relative;
background: center/contain no-repeat url(tech_preview.svg);
width: 26px;
height: 23px;
}
td.memItemRight span.status {
margin-top: -10px;
right: -10px;
top: 6px;
}
.qml {
display: block;
margin: 10px;

View File

@ -505,6 +505,18 @@ h1,h2,h3,h4,h5,h6 {
font-size:150%;
margin-bottom: 1em
}
span.status.technical-preview {
display:inline-block;
position:relative;
background:center/contain no-repeat url(tech_preview.svg);
width:26px;
height:23px
}
td.memItemRight span.status {
margin-top:-10px;
right:-10px;
top:6px
}
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section {
display:block
}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="49" height="43" viewBox="0 0 13 11" stroke="#09102b"><path d="M12.82 11.125L.155 11.121 6.491.155z" opacity=".999" fill="#ffe353" stroke-linejoin="round" stroke-width=".31"/><path d="M4.914 7.071L3.521 8.302l1.287 1.084m1.144.027l1.394-1.231-1.287-1.084m-.283-.539l-.788 2.974" fill="none" stroke-linecap="round" stroke-width=".151"/><path d="M7.287 9.358l-.013.259 1.695.025-.021-.297-1.662.013zm.706.017L7.989 5.7l.367-.004.017 3.658m.33-3.512l-.238.008.004.255.238.033-.004-.297zm-.221.05l-3.199.008.004.238 3.165-.021.029-.226zm-2.965.255l.025.447.28-.15.23.163-.267.313m-.23-.322l-.242-.046-.084.163.288.226" fill="#41cd52" stroke-width=".151"/></svg>

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 222 KiB

View File

@ -5,7 +5,6 @@ qt_examples_build_begin(EXTERNAL_BUILD)
add_subdirectory(corelib)
add_subdirectory(embedded)
add_subdirectory(qpa)
if(TARGET Qt6::DBus)
add_subdirectory(dbus)
endif()

View File

@ -6,9 +6,6 @@ add_subdirectory(mimetypes)
add_subdirectory(serialization)
add_subdirectory(tools)
add_subdirectory(platform)
if(QT_FEATURE_permissions)
add_subdirectory(permissions)
endif()
if(QT_FEATURE_thread)
add_subdirectory(threads)
endif()

View File

@ -38,8 +38,7 @@ double BindableSubscription::calculateDiscount() const
case Yearly:
return 0.6;
}
Q_ASSERT(false);
return -1;
Q_UNREACHABLE_RETURN(-1);
}
int BindableSubscription::basePrice() const

View File

@ -11,6 +11,10 @@
#include <QPushButton>
#include <QRadioButton>
#include <QSpinBox>
#include <QProperty>
#include <QString>
#include <QDateTimeEdit>
#include <QBindable>
int main(int argc, char *argv[])
{
@ -19,6 +23,8 @@ int main(int argc, char *argv[])
BindableSubscription subscription(&user);
SubscriptionWindow w;
// clazy:excludeall=lambda-in-connect
// when subscription is out of scope so is window
// Initialize subscription data
QRadioButton *monthly = w.findChild<QRadioButton *>("btnMonthly");
@ -49,9 +55,8 @@ int main(int argc, char *argv[])
});
QSpinBox *ageSpinBox = w.findChild<QSpinBox *>("ageSpinBox");
QObject::connect(ageSpinBox, &QSpinBox::valueChanged, [&](int value) {
user.setAge(value);
});
QBindable<int> ageBindable(ageSpinBox, "value");
user.bindableAge().setBinding([ageBindable](){ return ageBindable.value();});
QLabel *priceDisplay = w.findChild<QLabel *>("priceDisplay");

View File

@ -4,6 +4,7 @@
/*!
\title Qt Android Notifier
\example platform/androidnotifier
\examplecategory {Mobile}
\brief Demonstrates calling Java code from Qt in an Android application.
\image androidnotifier.png

View File

@ -1,11 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_example(cbordump)
qt_internal_add_example(convert)
qt_internal_add_example(savegame)
if(TARGET Qt6::Network AND TARGET Qt6::Widgets)
qt_internal_add_example(rsslisting)
if(NOT ANDROID)
qt_internal_add_example(cbordump)
qt_internal_add_example(convert)
qt_internal_add_example(savegame)
endif()
if(TARGET Qt6::Widgets)
qt_internal_add_example(streambookmarks)

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(cbordump LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -3,7 +3,8 @@
/*!
\example serialization/cbordump
\examplecategory {Input/Output}
\examplecategory {Data Processing & I/O}
\meta tag {network}
\title Parsing and displaying CBOR data
\brief A demonstration of how to parse files in CBOR format.

View File

@ -14,6 +14,8 @@
#include <stdarg.h>
#include <stdio.h>
using namespace Qt::StringLiterals;
/*
* To regenerate:
* curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml
@ -33,7 +35,7 @@
struct CborTagDescription
{
QCborTag tag;
const char *description; // with space and parentheses
const char *description; // with space and parentheses
};
// CBOR Tags
@ -216,22 +218,18 @@ static const CborTagDescription tagDescriptions[] = {
enum {
// See RFC 7049 section 2.
SmallValueBitLength = 5,
SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
Value8Bit = 24,
Value16Bit = 25,
Value32Bit = 26,
Value64Bit = 27
SmallValueBitLength = 5,
SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
Value8Bit = 24,
Value16Bit = 25,
Value32Bit = 26,
Value64Bit = 27
};
//! [0]
struct CborDumper
{
enum DumpOption {
ShowCompact = 0x01,
ShowWidthIndicators = 0x02,
ShowAnnotated = 0x04
};
enum DumpOption { ShowCompact = 0x01, ShowWidthIndicators = 0x02, ShowAnnotated = 0x04 };
Q_DECLARE_FLAGS(DumpOptions, DumpOption)
CborDumper(QFile *f, DumpOptions opts_);
@ -268,8 +266,7 @@ static int cborNumberSize(quint64 value)
return normalSize;
}
CborDumper::CborDumper(QFile *f, DumpOptions opts_)
: opts(opts_)
CborDumper::CborDumper(QFile *f, DumpOptions opts_) : opts(opts_)
{
// try to mmap the file, this is faster
char *ptr = reinterpret_cast<char *>(f->map(0, f->size(), QFile::MapPrivateOption));
@ -316,7 +313,8 @@ QCborError CborDumper::dump()
return err;
}
template <typename T> static inline bool canConvertTo(double v)
template<typename T>
static inline bool canConvertTo(double v)
{
using TypeInfo = std::numeric_limits<T>;
// The [conv.fpint] (7.10 Floating-integral conversions) section of the
@ -337,31 +335,32 @@ template <typename T> static inline bool canConvertTo(double v)
return v == floor(v);
}
static QString fpToString(double v, const char *suffix)
static QString fpToString(double v, QLatin1StringView suffix = ""_L1)
{
if (qIsInf(v))
return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf");
return v < 0 ? "-inf"_L1 : "inf"_L1;
if (qIsNaN(v))
return QStringLiteral("nan");
return "nan"_L1;
if (canConvertTo<qint64>(v))
return QString::number(qint64(v)) + ".0" + suffix;
return QString::number(qint64(v)) + ".0"_L1 + suffix;
if (canConvertTo<quint64>(v))
return QString::number(quint64(v)) + ".0" + suffix;
return QString::number(quint64(v)) + ".0"_L1 + suffix;
QString s = QString::number(v, 'g', QLocale::FloatingPointShortest);
if (!s.contains('.') && !s.contains('e'))
s += '.';
s += suffix;
if (!s.contains(u'.') && !s.contains(u'e'))
s += u'.';
if (suffix.size())
s += suffix;
return s;
};
void CborDumper::dumpOne(int nestingLevel)
{
QString indent(1, QLatin1Char(' '));
QString indent(1, u' ');
QString indented = indent;
if (!opts.testFlag(ShowCompact)) {
indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' '));
indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' '));
indent = u'\n' + QString(4 * nestingLevel, u' ');
indented = u'\n' + QString(4 + 4 * nestingLevel, u' ');
}
switch (reader.type()) {
@ -401,7 +400,7 @@ void CborDumper::dumpOne(int nestingLevel)
printStringWidthIndicator(r.data.size());
r = reader.readByteArray();
comma = QLatin1Char(',') + indented;
comma = u',' + indented;
}
} else {
auto r = reader.readString();
@ -410,7 +409,7 @@ void CborDumper::dumpOne(int nestingLevel)
printStringWidthIndicator(r.data.toUtf8().size());
r = reader.readString();
comma = QLatin1Char(',') + indented;
comma = u',' + indented;
}
}
@ -466,7 +465,7 @@ void CborDumper::dumpOne(int nestingLevel)
if (reader.next()) {
printWidthIndicator(quint64(tag));
printf("(");
dumpOne(nestingLevel); // same level!
dumpOne(nestingLevel); // same level!
printf(")");
}
@ -498,15 +497,15 @@ void CborDumper::dumpOne(int nestingLevel)
break;
case QCborStreamReader::Float16:
printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16")));
printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16"_L1)));
reader.next();
break;
case QCborStreamReader::Float:
printf("%s", qPrintable(fpToString(reader.toFloat(), "f")));
printf("%s", qPrintable(fpToString(reader.toFloat(), "f"_L1)));
reader.next();
break;
case QCborStreamReader::Double:
printf("%s", qPrintable(fpToString(reader.toDouble(), "")));
printf("%s", qPrintable(fpToString(reader.toDouble())));
reader.next();
break;
case QCborStreamReader::Invalid:
@ -559,7 +558,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
};
auto printFp = [=](const char *descr, double d) {
QString s = fpToString(d, "");
QString s = fpToString(d);
if (s.size() <= 6)
return print(descr, "%s", qPrintable(s));
return print(descr, "%a", d);
@ -574,7 +573,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
qsizetype size = reader.currentStringChunkSize();
if (size < 0)
return; // error
return; // error
if (size >= ChunkSizeLimit) {
fprintf(stderr, "String length too big, %lli\n", qint64(size));
exit(EXIT_FAILURE);
@ -619,7 +618,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
printf(" %s%s", indent.constData(), section.toHex(' ').constData());
// print the decode
QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' ');
QByteArray spaces(width > 0 ? width - section.size() * 3 + 1 : 0, ' ');
printf("%s # \"", spaces.constData());
auto ptr = reinterpret_cast<const uchar *>(section.constData());
for (int j = 0; j < section.size(); ++j)
@ -631,7 +630,7 @@ void CborDumper::dumpOneDetailed(int nestingLevel)
// get the next chunk
size = reader.currentStringChunkSize();
if (size < 0)
return; // error
return; // error
if (size >= ChunkSizeLimit) {
fprintf(stderr, "String length too big, %lli\n", qint64(size));
exit(EXIT_FAILURE);
@ -770,7 +769,9 @@ void CborDumper::printByteArray(const QByteArray &ba)
break;
case quint8(QCborKnownTags::ExpectedBase64url):
printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData());
printf("b64'%s'",
ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals)
.constData());
break;
}
}
@ -811,23 +812,20 @@ int main(int argc, char *argv[])
setlocale(LC_ALL, "C");
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool"));
parser.setApplicationDescription("CBOR Dumper tool"_L1);
parser.addHelpOption();
QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")},
QStringLiteral("Use compact form (no line breaks)"));
QCommandLineOption compact({"c"_L1, "compact"_L1}, "Use compact form (no line breaks)"_L1);
parser.addOption(compact);
QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")},
QStringLiteral("Show indicators for width of lengths and integrals"));
QCommandLineOption showIndicators({ "i"_L1, "indicators"_L1 },
"Show indicators for width of lengths and integrals"_L1);
parser.addOption(showIndicators);
QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")},
QStringLiteral("Show bytes and annotated decoding"));
QCommandLineOption verbose({"a"_L1, "annotated"_L1}, "Show bytes and annotated decoding"_L1);
parser.addOption(verbose);
parser.addPositionalArgument(QStringLiteral("[source]"),
QStringLiteral("CBOR file to read from"));
parser.addPositionalArgument("[source]"_L1, "CBOR file to read from"_L1);
parser.process(app);

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(convert LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
@ -18,6 +22,7 @@ qt_add_executable(convert
cborconverter.cpp cborconverter.h
converter.h
datastreamconverter.cpp datastreamconverter.h
debugtextdumper.cpp debugtextdumper.h
jsonconverter.cpp jsonconverter.h
main.cpp
nullconverter.cpp nullconverter.h

View File

@ -3,19 +3,21 @@
#include "cborconverter.h"
#include <QCborArray>
#include <QCborMap>
#include <QCborStreamReader>
#include <QCborStreamWriter>
#include <QCborMap>
#include <QCborArray>
#include <QCborValue>
#include <QDataStream>
#include <QFloat16>
#include <QFile>
#include <QFloat16>
#include <QMetaType>
#include <QTextStream>
#include <stdio.h>
using namespace Qt::StringLiterals;
static CborConverter cborConverter;
static CborDiagnosticDumper cborDiagnosticDumper;
@ -118,33 +120,33 @@ static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrim
}
//! [1]
QString CborDiagnosticDumper::name()
QString CborDiagnosticDumper::name() const
{
return QStringLiteral("cbor-dump");
return "cbor-dump"_L1;
}
Converter::Direction CborDiagnosticDumper::directions()
Converter::Directions CborDiagnosticDumper::directions() const
{
return Out;
return Direction::Out;
}
Converter::Options CborDiagnosticDumper::outputOptions()
Converter::Options CborDiagnosticDumper::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *CborDiagnosticDumper::optionsHelp()
const char *CborDiagnosticDumper::optionsHelp() const
{
return diagnosticHelp;
}
bool CborDiagnosticDumper::probeFile(QIODevice *f)
bool CborDiagnosticDumper::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant CborDiagnosticDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNREACHABLE();
Q_UNUSED(f);
@ -152,7 +154,8 @@ QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverte
return QVariant();
}
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped;
for (const QString &s : options) {
@ -181,8 +184,7 @@ void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, cons
}
QTextStream out(f);
out << convertFromVariant(contents, Double).toDiagnosticNotation(opts)
<< Qt::endl;
out << convertFromVariant(contents, Double).toDiagnosticNotation(opts) << Qt::endl;
}
CborConverter::CborConverter()
@ -190,37 +192,37 @@ CborConverter::CborConverter()
qRegisterMetaType<QCborTag>();
}
QString CborConverter::name()
QString CborConverter::name() const
{
return "cbor";
}
Converter::Direction CborConverter::directions()
Converter::Directions CborConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options CborConverter::outputOptions()
Converter::Options CborConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *CborConverter::optionsHelp()
const char *CborConverter::optionsHelp() const
{
return cborOptionHelp;
}
bool CborConverter::probeFile(QIODevice *f)
bool CborConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".cbor")))
if (file->fileName().endsWith(".cbor"_L1))
return true;
}
return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
}
//! [2]
QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant CborConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
const char *ptr = nullptr;
if (auto file = qobject_cast<QFile *>(f))
@ -256,7 +258,7 @@ QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
}
//! [2]
//! [3]
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) const
{
//! [3]
bool useSignature = true;
@ -318,8 +320,9 @@ void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStri
exit(EXIT_FAILURE);
}
//! [4]
QCborValue v = convertFromVariant(contents,
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
QCborValue v =
convertFromVariant(contents,
useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
QCborStreamWriter writer(f);
if (useSignature)
writer.append(QCborKnownTags::Signature);

View File

@ -10,13 +10,14 @@ class CborDiagnosticDumper : public Converter
{
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
class CborConverter : public Converter
@ -26,13 +27,14 @@ public:
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // CBORCONVERTER_H

View File

@ -12,17 +12,19 @@ INSTALLS += target
SOURCES += main.cpp \
cborconverter.cpp \
jsonconverter.cpp \
datastreamconverter.cpp \
debugtextdumper.cpp \
jsonconverter.cpp \
nullconverter.cpp \
textconverter.cpp \
xmlconverter.cpp \
nullconverter.cpp
xmlconverter.cpp
HEADERS += \
converter.h \
cborconverter.h \
jsonconverter.h \
datastreamconverter.h \
debugtextdumper.h \
jsonconverter.h \
nullconverter.h \
textconverter.h \
xmlconverter.h \
nullconverter.h
xmlconverter.h

View File

@ -5,10 +5,10 @@
#define CONVERTER_H
#include <QIODevice>
#include <QList>
#include <QPair>
#include <QVariant>
#include <QVariantMap>
#include <QList>
class VariantOrderedMap : public QList<QPair<QVariant, QVariant>>
{
@ -32,26 +32,25 @@ protected:
public:
static Converter *null;
enum Direction {
In = 1, Out = 2, InOut = 3
};
enum class Direction { In = 1, Out = 2, InOut = In | Out };
Q_DECLARE_FLAGS(Directions, Direction)
enum Option {
SupportsArbitraryMapKeys = 0x01
};
enum Option { SupportsArbitraryMapKeys = 0x01 };
Q_DECLARE_FLAGS(Options, Option)
virtual ~Converter() = 0;
virtual QString name() = 0;
virtual Direction directions() = 0;
virtual Options outputOptions() = 0;
virtual const char *optionsHelp() = 0;
virtual bool probeFile(QIODevice *f) = 0;
virtual QVariant loadFile(QIODevice *f, Converter *&outputConverter) = 0;
virtual void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) = 0;
virtual QString name() const = 0;
virtual Directions directions() const = 0;
virtual Options outputOptions() const = 0;
virtual const char *optionsHelp() const = 0;
virtual bool probeFile(QIODevice *f) const = 0;
virtual QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const = 0;
virtual void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const = 0;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Directions)
Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
#endif // CONVERTER_H

View File

@ -2,20 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "datastreamconverter.h"
#include "debugtextdumper.h"
#include <QDataStream>
#include <QDebug>
#include <QTextStream>
using namespace Qt::StringLiterals;
static const char dataStreamOptionHelp[] =
"byteorder=host|big|little Byte order to use.\n"
"version=<n> QDataStream version (default: Qt 5.0).\n"
"version=<n> QDataStream version (default: Qt 6.0).\n"
;
static const char signature[] = "qds";
static DataStreamDumper dataStreamDumper;
static DataStreamConverter DataStreamConverter;
static DataStreamConverter dataStreamConverter;
static DebugTextDumper debugTextDumper;
QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map)
{
@ -42,123 +43,43 @@ QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map)
return ds;
}
static QString dumpVariant(const QVariant &v, const QString &indent = QLatin1String("\n"))
{
QString result;
QString indented = indent + QLatin1String(" ");
int type = v.userType();
if (type == qMetaTypeId<VariantOrderedMap>() || type == QMetaType::QVariantMap) {
const auto map = (type == QMetaType::QVariantMap) ?
VariantOrderedMap(v.toMap()) : qvariant_cast<VariantOrderedMap>(v);
result = QLatin1String("Map {");
for (const auto &pair : map) {
result += indented + dumpVariant(pair.first, indented);
result.chop(1); // remove comma
result += QLatin1String(" => ") + dumpVariant(pair.second, indented);
}
result.chop(1); // remove comma
result += indent + QLatin1String("},");
} else if (type == QMetaType::QVariantList) {
const QVariantList list = v.toList();
result = QLatin1String("List [");
for (const auto &item : list)
result += indented + dumpVariant(item, indented);
result.chop(1); // remove comma
result += indent + QLatin1String("],");
} else {
QDebug debug(&result);
debug.nospace() << v << ',';
}
return result;
}
QString DataStreamDumper::name()
{
return QStringLiteral("datastream-dump");
}
Converter::Direction DataStreamDumper::directions()
{
return Out;
}
Converter::Options DataStreamDumper::outputOptions()
{
return SupportsArbitraryMapKeys;
}
const char *DataStreamDumper::optionsHelp()
{
return nullptr;
}
bool DataStreamDumper::probeFile(QIODevice *f)
{
Q_UNUSED(f);
return false;
}
QVariant DataStreamDumper::loadFile(QIODevice *f, Converter *&outputConverter)
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void DataStreamDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
{
Q_UNUSED(options);
QString s = dumpVariant(contents);
s[s.size() - 1] = QLatin1Char('\n'); // replace the comma with newline
QTextStream out(f);
out << s;
}
DataStreamConverter::DataStreamConverter()
{
qRegisterMetaType<VariantOrderedMap>();
}
QString DataStreamConverter::name()
QString DataStreamConverter::name() const
{
return QStringLiteral("datastream");
return "datastream"_L1;
}
Converter::Direction DataStreamConverter::directions()
Converter::Directions DataStreamConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options DataStreamConverter::outputOptions()
Converter::Options DataStreamConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *DataStreamConverter::optionsHelp()
const char *DataStreamConverter::optionsHelp() const
{
return dataStreamOptionHelp;
}
bool DataStreamConverter::probeFile(QIODevice *f)
bool DataStreamConverter::probeFile(QIODevice *f) const
{
return f->isReadable() && f->peek(sizeof(signature) - 1) == signature;
}
QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant DataStreamConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = &dataStreamDumper;
outputConverter = &debugTextDumper;
char c;
if (f->read(sizeof(signature) -1) != signature ||
!f->getChar(&c) || (c != 'l' && c != 'B')) {
if (f->read(sizeof(signature) - 1) != signature || !f->getChar(&c) || (c != 'l' && c != 'B')) {
fprintf(stderr, "Could not load QDataStream file: invalid signature.\n");
exit(EXIT_FAILURE);
}
@ -175,9 +96,10 @@ QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter
return result;
}
void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
QDataStream::Version version = QDataStream::Qt_5_0;
QDataStream::Version version = QDataStream::Qt_6_0;
auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder);
for (const QString &option : options) {
const QStringList pair = option.split('=');
@ -213,7 +135,7 @@ void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const
exit(EXIT_FAILURE);
}
char c = order == QDataStream::LittleEndian ? 'l' : 'B';
char c = order == QDataStream::LittleEndian ? 'l' : 'B';
f->write(signature);
f->write(&c, 1);

View File

@ -6,19 +6,6 @@
#include "converter.h"
class DataStreamDumper : public Converter
{
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
};
class DataStreamConverter : public Converter
{
public:
@ -26,13 +13,14 @@ public:
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // DATASTREAMCONVERTER_H

View File

@ -0,0 +1,89 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "debugtextdumper.h"
#include <QDebug>
#include <QTextStream>
using namespace Qt::StringLiterals;
// Static instance is declared in datastreamconverter.cpp, since it uses it.
static QString dumpVariant(const QVariant &v, const QString &indent = "\n"_L1)
{
QString result;
QString indented = indent + " "_L1;
int type = v.userType();
if (type == qMetaTypeId<VariantOrderedMap>() || type == QMetaType::QVariantMap) {
const auto map = (type == QMetaType::QVariantMap) ? VariantOrderedMap(v.toMap())
: qvariant_cast<VariantOrderedMap>(v);
result = "Map {"_L1;
for (const auto &pair : map) {
result += indented + dumpVariant(pair.first, indented);
result.chop(1); // remove comma
result += " => "_L1 + dumpVariant(pair.second, indented);
}
result.chop(1); // remove comma
result += indent + "},"_L1;
} else if (type == QMetaType::QVariantList) {
const QVariantList list = v.toList();
result = "List ["_L1;
for (const auto &item : list)
result += indented + dumpVariant(item, indented);
result.chop(1); // remove comma
result += indent + "],"_L1;
} else {
QDebug debug(&result);
debug.nospace() << v << ',';
}
return result;
}
QString DebugTextDumper::name() const
{
return "debugtext-dump"_L1;
}
Converter::Directions DebugTextDumper::directions() const
{
return Direction::Out;
}
Converter::Options DebugTextDumper::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *DebugTextDumper::optionsHelp() const
{
return nullptr;
}
bool DebugTextDumper::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant DebugTextDumper::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNREACHABLE();
Q_UNUSED(f);
Q_UNUSED(outputConverter);
return QVariant();
}
void DebugTextDumper::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
Q_UNUSED(options);
QString s = dumpVariant(contents);
s[s.size() - 1] = u'\n'; // replace the comma with newline
QTextStream out(f);
out << s;
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef DEBUGTEXTDUMPER_H
#define DEBUGTEXTDUMPER_H
#include "converter.h"
class DebugTextDumper : public Converter
{
// Converter interface
public:
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // DEBUGTEXTDUMPER_H

View File

@ -3,7 +3,8 @@
/*!
\example serialization/convert
\examplecategory {Input/Output}
\examplecategory {Data Processing & I/O}
\meta tag {network}
\title Convert Example
\brief The Convert example demonstrates how to convert between different
@ -59,7 +60,7 @@
\section1 The DataStreamConverter Class
The DataStreamConverter class is used to serialize to and from the
QDataStream format. There is also the DataStreamDumper class for outputting
QDataStream format. There is also the DebugTextDumper class for outputting
the data lossless in a non-standardized human readable format.
\section1 The JsonConverter Class

View File

@ -9,10 +9,11 @@
#include <QJsonObject>
#include <QJsonValue>
using namespace Qt::StringLiterals;
static JsonConverter jsonConverter;
static const char jsonOptionHelp[] =
"compact=no|yes Use compact JSON form.\n";
static const char jsonOptionHelp[] = "compact=no|yes Use compact JSON form.\n";
static QJsonDocument convertFromVariant(const QVariant &v)
{
@ -24,34 +25,30 @@ static QJsonDocument convertFromVariant(const QVariant &v)
return doc;
}
JsonConverter::JsonConverter()
QString JsonConverter::name() const
{
return "json"_L1;
}
QString JsonConverter::name()
Converter::Directions JsonConverter::directions() const
{
return "json";
return Direction::InOut;
}
Converter::Direction JsonConverter::directions()
{
return InOut;
}
Converter::Options JsonConverter::outputOptions()
Converter::Options JsonConverter::outputOptions() const
{
return {};
}
const char *JsonConverter::optionsHelp()
const char *JsonConverter::optionsHelp() const
{
return jsonOptionHelp;
}
bool JsonConverter::probeFile(QIODevice *f)
bool JsonConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".json")))
if (file->fileName().endsWith(".json"_L1))
return true;
}
@ -62,7 +59,7 @@ bool JsonConverter::probeFile(QIODevice *f)
return false;
}
QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant JsonConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = this;
@ -87,13 +84,14 @@ QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
return doc.toVariant();
}
void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void JsonConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
QJsonDocument::JsonFormat format = QJsonDocument::Indented;
for (const QString &s : options) {
if (s == QLatin1String("compact=no")) {
if (s == "compact=no"_L1) {
format = QJsonDocument::Indented;
} else if (s == QLatin1String("compact=yes")) {
} else if (s == "compact=yes"_L1) {
format = QJsonDocument::Compact;
} else {
fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s",

View File

@ -8,18 +8,16 @@
class JsonConverter : public Converter
{
public:
JsonConverter();
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // JSONCONVERTER_H

View File

@ -11,12 +11,14 @@
#include <stdio.h>
static QList<Converter *> *availableConverters;
using namespace Qt::StringLiterals;
static QList<const Converter *> *availableConverters;
Converter::Converter()
{
if (!availableConverters)
availableConverters = new QList<Converter *>;
availableConverters = new QList<const Converter *>;
availableConverters->append(this);
}
@ -31,64 +33,68 @@ int main(int argc, char *argv[])
QStringList inputFormats;
QStringList outputFormats;
for (Converter *conv : std::as_const(*availableConverters)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
auto direction = conv->directions();
QString name = conv->name();
if (direction & Converter::In)
if (direction.testFlag(Converter::Direction::In))
inputFormats << name;
if (direction & Converter::Out)
if (direction.testFlag(Converter::Direction::Out))
outputFormats << name;
}
inputFormats.sort();
outputFormats.sort();
inputFormats.prepend("auto");
outputFormats.prepend("auto");
inputFormats.prepend("auto"_L1);
outputFormats.prepend("auto"_L1);
QCommandLineParser parser;
parser.setApplicationDescription(QStringLiteral("Qt file format conversion tool"));
parser.setApplicationDescription("Qt file format conversion tool"_L1);
parser.addHelpOption();
QCommandLineOption inputFormatOption(QStringList{"I", "input-format"});
inputFormatOption.setDescription(QLatin1String("Select the input format for the input file. Available formats: ") +
inputFormats.join(", "));
inputFormatOption.setValueName("format");
QCommandLineOption inputFormatOption(QStringList{ "I"_L1, "input-format"_L1 });
inputFormatOption.setDescription(
"Select the input format for the input file. Available formats: "_L1
+ inputFormats.join(", "_L1));
inputFormatOption.setValueName("format"_L1);
inputFormatOption.setDefaultValue(inputFormats.constFirst());
parser.addOption(inputFormatOption);
QCommandLineOption outputFormatOption(QStringList{"O", "output-format"});
outputFormatOption.setDescription(QLatin1String("Select the output format for the output file. Available formats: ") +
outputFormats.join(", "));
outputFormatOption.setValueName("format");
QCommandLineOption outputFormatOption(QStringList{ "O"_L1, "output-format"_L1 });
outputFormatOption.setDescription(
"Select the output format for the output file. Available formats: "_L1
+ outputFormats.join(", "_L1));
outputFormatOption.setValueName("format"_L1);
outputFormatOption.setDefaultValue(outputFormats.constFirst());
parser.addOption(outputFormatOption);
QCommandLineOption optionOption(QStringList{"o", "option"});
optionOption.setDescription(QStringLiteral("Format-specific options. Use --format-options to find out what options are available."));
optionOption.setValueName("options...");
QCommandLineOption optionOption(QStringList{ "o"_L1, "option"_L1 });
optionOption.setDescription(
"Format-specific options. Use --format-options to find out what options are available."_L1);
optionOption.setValueName("options..."_L1);
optionOption.setDefaultValues({});
parser.addOption(optionOption);
QCommandLineOption formatOptionsOption("format-options");
formatOptionsOption.setDescription(QStringLiteral("Prints the list of valid options for --option for the converter format <format>."));
formatOptionsOption.setValueName("format");
QCommandLineOption formatOptionsOption("format-options"_L1);
formatOptionsOption.setDescription(
"Prints the list of valid options for --option for the converter format <format>."_L1);
formatOptionsOption.setValueName("format"_L1);
parser.addOption(formatOptionsOption);
parser.addPositionalArgument(QStringLiteral("[source]"),
QStringLiteral("File to read from (stdin if none)"));
parser.addPositionalArgument(QStringLiteral("[destination]"),
QStringLiteral("File to write to (stdout if none)"));
parser.addPositionalArgument("[source]"_L1, "File to read from (stdin if none)"_L1);
parser.addPositionalArgument("[destination]"_L1, "File to write to (stdout if none)"_L1);
parser.process(app);
if (parser.isSet(formatOptionsOption)) {
QString format = parser.value(formatOptionsOption);
for (Converter *conv : std::as_const(*availableConverters)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
const char *help = conv->optionsHelp();
if (help)
printf("The following options are available for format '%s':\n\n%s", qPrintable(format), help);
else
if (help) {
printf("The following options are available for format '%s':\n\n%s",
qPrintable(format), help);
} else {
printf("Format '%s' supports no options.\n", qPrintable(format));
}
return EXIT_SUCCESS;
}
}
@ -97,10 +103,10 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
Converter *inconv = nullptr;
const Converter *inconv = nullptr;
QString format = parser.value(inputFormatOption);
if (format != "auto") {
for (Converter *conv : std::as_const(*availableConverters)) {
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
inconv = conv;
break;
@ -113,10 +119,10 @@ int main(int argc, char *argv[])
}
}
Converter *outconv = nullptr;
const Converter *outconv = nullptr;
format = parser.value(outputFormatOption);
if (format != "auto") {
for (Converter *conv : std::as_const(*availableConverters)) {
if (format != "auto"_L1) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->name() == format) {
outconv = conv;
break;
@ -155,8 +161,9 @@ int main(int argc, char *argv[])
if (!inconv) {
// probe the input to find a file format
for (Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions() & Converter::In && conv->probeFile(&input)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::In)
&& conv->probeFile(&input)) {
inconv = conv;
break;
}
@ -170,8 +177,9 @@ int main(int argc, char *argv[])
if (!outconv) {
// probe the output to find a file format
for (Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions() & Converter::Out && conv->probeFile(&output)) {
for (const Converter *conv : std::as_const(*availableConverters)) {
if (conv->directions().testFlag(Converter::Direction::Out)
&& conv->probeFile(&output)) {
outconv = conv;
break;
}

View File

@ -3,36 +3,38 @@
#include "nullconverter.h"
using namespace Qt::StringLiterals;
static NullConverter nullConverter;
Converter* Converter::null = &nullConverter;
Converter *Converter::null = &nullConverter;
QString NullConverter::name()
QString NullConverter::name() const
{
return QLatin1String("null");
return "null"_L1;
}
Converter::Direction NullConverter::directions()
Converter::Directions NullConverter::directions() const
{
return Out;
return Direction::Out;
}
Converter::Options NullConverter::outputOptions()
Converter::Options NullConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *NullConverter::optionsHelp()
const char *NullConverter::optionsHelp() const
{
return nullptr;
}
bool NullConverter::probeFile(QIODevice *f)
bool NullConverter::probeFile(QIODevice *f) const
{
Q_UNUSED(f);
return false;
}
QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant NullConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
Q_UNUSED(f);
Q_UNUSED(outputConverter);
@ -40,10 +42,12 @@ QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter)
return QVariant();
}
void NullConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void NullConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
if (!options.isEmpty()) {
fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qPrintable(options.first()));
fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n",
qPrintable(options.first()));
exit(EXIT_FAILURE);
}

View File

@ -10,13 +10,14 @@ class NullConverter : public Converter
{
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // NULLCONVERTER_H

View File

@ -6,6 +6,8 @@
#include <QFile>
#include <QTextStream>
using namespace Qt::StringLiterals;
static void dumpVariant(QTextStream &out, const QVariant &v)
{
switch (v.userType()) {
@ -42,67 +44,62 @@ static void dumpVariant(QTextStream &out, const QVariant &v)
}
}
QString TextConverter::name()
QString TextConverter::name() const
{
return QStringLiteral("text");
return "text"_L1;
}
Converter::Direction TextConverter::directions()
Converter::Directions TextConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options TextConverter::outputOptions()
Converter::Options TextConverter::outputOptions() const
{
return {};
}
const char *TextConverter::optionsHelp()
const char *TextConverter::optionsHelp() const
{
return nullptr;
}
bool TextConverter::probeFile(QIODevice *f)
bool TextConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f))
return file->fileName().endsWith(QLatin1String(".txt"));
return file->fileName().endsWith(".txt"_L1);
return false;
}
QVariant TextConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant TextConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = this;
QVariantList list;
QTextStream in(f);
QString line ;
QString line;
while (!in.atEnd()) {
in.readLineInto(&line);
bool ok;
qint64 v = line.toLongLong(&ok);
if (ok) {
if (qint64 v = line.toLongLong(&ok); ok)
list.append(v);
continue;
}
double d = line.toDouble(&ok);
if (ok) {
else if (double d = line.toDouble(&ok); ok)
list.append(d);
continue;
}
list.append(line);
else
list.append(line);
}
return list;
}
void TextConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void TextConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
if (!options.isEmpty()) {
fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qPrintable(options.first()));
fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n",
qPrintable(options.first()));
exit(EXIT_FAILURE);
}

View File

@ -8,16 +8,16 @@
class TextConverter : public Converter
{
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // TEXTCONVERTER_H

View File

@ -13,8 +13,9 @@
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
static const char xmlOptionHelp[] =
"compact=no|yes Use compact XML form.\n";
using namespace Qt::StringLiterals;
static const char xmlOptionHelp[] = "compact=no|yes Use compact XML form.\n";
static XmlConverter xmlConverter;
@ -23,7 +24,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options options)
{
QVariantList list;
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("list"))) {
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "list"_L1)) {
xml.readNext();
switch (xml.tokenType()) {
case QXmlStreamReader::StartElement:
@ -47,8 +48,7 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
break;
}
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
}
@ -57,10 +57,11 @@ static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options option
return list;
}
static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Converter::Options options)
static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml,
Converter::Options options)
{
QVariant key, value;
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("entry"))) {
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "entry"_L1)) {
xml.readNext();
switch (xml.tokenType()) {
case QXmlStreamReader::StartElement:
@ -89,8 +90,7 @@ static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Conv
break;
}
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
}
@ -103,11 +103,11 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
QVariantMap map1;
VariantOrderedMap map2;
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == QLatin1String("map"))) {
while (!xml.atEnd() && !(xml.isEndElement() && xml.name() == "map"_L1)) {
xml.readNext();
switch (xml.tokenType()) {
case QXmlStreamReader::StartElement:
if (xml.name() == QLatin1String("entry")) {
if (xml.name() == "entry"_L1) {
auto pair = mapEntryFromXml(xml, options);
if (options & Converter::SupportsArbitraryMapKeys)
map2.append(pair);
@ -134,8 +134,7 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
break;
}
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
exit(EXIT_FAILURE);
}
@ -149,18 +148,18 @@ static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options)
{
QStringView name = xml.name();
if (name == QLatin1String("list"))
if (name == "list"_L1)
return listFromXml(xml, options);
if (name == QLatin1String("map"))
if (name == "map"_L1)
return mapFromXml(xml, options);
if (name != QLatin1String("value")) {
if (name != "value"_L1) {
fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n",
xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
exit(EXIT_FAILURE);
}
QXmlStreamAttributes attrs = xml.attributes();
QStringView type = attrs.value(QLatin1String("type"));
QStringView type = attrs.value("type"_L1);
forever {
xml.readNext();
@ -169,8 +168,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement())
break;
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(name.toString()));
exit(EXIT_FAILURE);
}
@ -180,45 +178,45 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
text = text.trimmed();
QVariant result;
bool ok;
if (type.isEmpty()) {
// ok
} else if (type == QLatin1String("number")) {
} else if (type == "number"_L1) {
// try integer first
bool ok;
qint64 v = text.toLongLong(&ok);
if (ok) {
result = v;
} else {
// let's see floating point
double d = text.toDouble(&ok);
result = d;
if (!ok) {
fprintf(stderr, "%lld:%lld: Invalid XML: could not interpret '%s' as a number.\n",
xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
exit(EXIT_FAILURE);
}
result = d;
}
} else if (type == QLatin1String("bytes")) {
} else if (type == "bytes"_L1) {
QByteArray data = text.toLatin1();
QStringView encoding = attrs.value("encoding");
if (encoding == QLatin1String("base64url")) {
if (encoding == "base64url"_L1) {
result = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding);
} else if (encoding == QLatin1String("hex")) {
} else if (encoding == "hex"_L1) {
result = QByteArray::fromHex(data);
} else if (encoding.isEmpty() || encoding == QLatin1String("base64")) {
} else if (encoding.isEmpty() || encoding == "base64"_L1) {
result = QByteArray::fromBase64(data);
} else {
fprintf(stderr, "%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.\n",
xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString()));
exit(EXIT_FAILURE);
}
} else if (type == QLatin1String("string")) {
} else if (type == "string"_L1) {
result = text.toString();
} else if (type == QLatin1String("null")) {
} else if (type == "null"_L1) {
result = QVariant::fromValue(nullptr);
} else if (type == QLatin1String("CBOR simple type")) {
} else if (type == "CBOR simple type"_L1) {
result = QVariant::fromValue(QCborSimpleType(text.toShort()));
} else if (type == QLatin1String("bits")) {
} else if (type == "bits"_L1) {
QBitArray ba;
ba.resize(text.size());
qsizetype n = 0;
@ -238,13 +236,13 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
result = ba;
} else {
int id = QMetaType::UnknownType;
if (type == QLatin1String("datetime"))
if (type == "datetime"_L1)
id = QMetaType::QDateTime;
else if (type == QLatin1String("url"))
else if (type == "url"_L1)
id = QMetaType::QUrl;
else if (type == QLatin1String("uuid"))
else if (type == "uuid"_L1)
id = QMetaType::QUuid;
else if (type == QLatin1String("regex"))
else if (type == "regex"_L1)
id = QMetaType::QRegularExpression;
else
id = QMetaType::fromName(type.toLatin1()).id();
@ -267,8 +265,7 @@ static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options
} while (xml.isComment() || xml.isWhitespace());
if (!xml.isEndElement()) {
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
xml.lineNumber(), xml.columnNumber(),
fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n", xml.lineNumber(), xml.columnNumber(),
qPrintable(xml.tokenString()), qPrintable(name.toString()));
exit(EXIT_FAILURE);
}
@ -287,9 +284,9 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
variantToXml(xml, v);
xml.writeEndElement();
} else if (type == QMetaType::QVariantMap || type == qMetaTypeId<VariantOrderedMap>()) {
const VariantOrderedMap map = (type == QMetaType::QVariantMap) ?
VariantOrderedMap(v.toMap()) :
qvariant_cast<VariantOrderedMap>(v);
const VariantOrderedMap map = (type == QMetaType::QVariantMap)
? VariantOrderedMap(v.toMap())
: qvariant_cast<VariantOrderedMap>(v);
xml.writeStartElement("map");
for (const auto &pair : map) {
@ -301,7 +298,7 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
xml.writeEndElement();
} else {
xml.writeStartElement("value");
QString typeString = QStringLiteral("type");
QString typeString = "type"_L1;
switch (type) {
case QMetaType::Short:
case QMetaType::UShort:
@ -399,37 +396,37 @@ static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
}
}
QString XmlConverter::name()
QString XmlConverter::name() const
{
return QStringLiteral("xml");
return "xml"_L1;
}
Converter::Direction XmlConverter::directions()
Converter::Directions XmlConverter::directions() const
{
return InOut;
return Direction::InOut;
}
Converter::Options XmlConverter::outputOptions()
Converter::Options XmlConverter::outputOptions() const
{
return SupportsArbitraryMapKeys;
}
const char *XmlConverter::optionsHelp()
const char *XmlConverter::optionsHelp() const
{
return xmlOptionHelp;
}
bool XmlConverter::probeFile(QIODevice *f)
bool XmlConverter::probeFile(QIODevice *f) const
{
if (QFile *file = qobject_cast<QFile *>(f)) {
if (file->fileName().endsWith(QLatin1String(".xml")))
if (file->fileName().endsWith(".xml"_L1))
return true;
}
return f->isReadable() && f->peek(5) == "<?xml";
}
QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter)
QVariant XmlConverter::loadFile(QIODevice *f, const Converter *&outputConverter) const
{
if (!outputConverter)
outputConverter = this;
@ -445,13 +442,14 @@ QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter)
return v;
}
void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
void XmlConverter::saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const
{
bool compact = false;
for (const QString &s : options) {
if (s == QLatin1String("compact=no")) {
if (s == "compact=no"_L1) {
compact = false;
} else if (s == QLatin1String("compact=yes")) {
} else if (s == "compact=yes"_L1) {
compact = true;
} else {
fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s",

View File

@ -10,13 +10,14 @@ class XmlConverter : public Converter
{
// Converter interface
public:
QString name() override;
Direction directions() override;
Options outputOptions() override;
const char *optionsHelp() override;
bool probeFile(QIODevice *f) override;
QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
QString name() const override;
Directions directions() const override;
Options outputOptions() const override;
const char *optionsHelp() const override;
bool probeFile(QIODevice *f) const override;
QVariant loadFile(QIODevice *f, const Converter *&outputConverter) const override;
void saveFile(QIODevice *f, const QVariant &contents,
const QStringList &options) const override;
};
#endif // XMLCONVERTER_H

View File

@ -1,26 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/*
main.cpp
Provides the main function for the RSS news reader example.
*/
#include <QtWidgets>
#include "rsslisting.h"
/*!
Create an application and a main widget. Open the main widget for
user input, and exit with an appropriate return value when it is
closed.
*/
int main(int argc, char **argv)
{
QApplication app(argc, argv);
RSSListing *rsslisting = new RSSListing;
rsslisting->show();
return app.exec();
}

View File

@ -1,211 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
/*
rsslisting.cpp
Provides a widget for displaying news items from RDF news sources.
RDF is an XML-based format for storing items of information (see
http://www.w3.org/RDF/ for details).
The widget itself provides a simple user interface for specifying
the URL of a news source, and controlling the downloading of news.
The widget downloads and parses the XML asynchronously, feeding the
data to an XML reader in pieces. This allows the user to interrupt
its operation, and also allows very large data sources to be read.
*/
#include <QtCore>
#include <QtWidgets>
#include <QtNetwork>
#include "rsslisting.h"
/*
Constructs an RSSListing widget with a simple user interface, and sets
up the XML reader to use a custom handler class.
The user interface consists of a line edit, a push button, and a
list view widget. The line edit is used for entering the URLs of news
sources; the push button starts the process of reading the
news.
*/
RSSListing::RSSListing(QWidget *parent)
: QWidget(parent), currentReply(0)
{
lineEdit = new QLineEdit(this);
lineEdit->setText("http://blog.qt.io/feed/");
fetchButton = new QPushButton(tr("Fetch"), this);
treeWidget = new QTreeWidget(this);
connect(treeWidget, &QTreeWidget::itemActivated,
this, &RSSListing::itemActivated);
QStringList headerLabels;
headerLabels << tr("Title") << tr("Link");
treeWidget->setHeaderLabels(headerLabels);
treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(&manager, &QNetworkAccessManager::finished,
this, &RSSListing::finished);
connect(lineEdit, &QLineEdit::returnPressed, this, &RSSListing::fetch);
connect(fetchButton, &QPushButton::clicked, this, &RSSListing::fetch);
QVBoxLayout *layout = new QVBoxLayout(this);
QHBoxLayout *hboxLayout = new QHBoxLayout;
hboxLayout->addWidget(lineEdit);
hboxLayout->addWidget(fetchButton);
layout->addLayout(hboxLayout);
layout->addWidget(treeWidget);
setWindowTitle(tr("RSS listing example"));
resize(640,480);
}
/*
Starts the network request and connects the needed signals
*/
void RSSListing::get(const QUrl &url)
{
QNetworkRequest request(url);
if (currentReply) {
currentReply->disconnect(this);
currentReply->deleteLater();
}
currentReply = manager.get(request);
connect(currentReply, &QNetworkReply::readyRead, this, &RSSListing::readyRead);
connect(currentReply, &QNetworkReply::metaDataChanged, this, &RSSListing::metaDataChanged);
connect(currentReply, &QNetworkReply::errorOccurred, this, &RSSListing::error);
}
/*
Starts fetching data from a news source specified in the line
edit widget.
The line edit is made read only to prevent the user from modifying its
contents during the fetch; this is only for cosmetic purposes.
The fetch button is disabled, the list view is cleared, and we
define the last list view item to be 0, meaning that there are no
existing items in the list.
A URL is created with the raw contents of the line edit and
a get is initiated.
*/
void RSSListing::fetch()
{
lineEdit->setReadOnly(true);
fetchButton->setEnabled(false);
treeWidget->clear();
xml.clear();
QUrl url(lineEdit->text());
get(url);
}
void RSSListing::metaDataChanged()
{
QUrl redirectionTarget = currentReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (redirectionTarget.isValid()) {
get(redirectionTarget);
}
}
/*
Reads data received from the RDF source.
We read all the available data, and pass it to the XML
stream reader. Then we call the XML parsing function.
*/
void RSSListing::readyRead()
{
int statusCode = currentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode >= 200 && statusCode < 300) {
QByteArray data = currentReply->readAll();
xml.addData(data);
parseXml();
}
}
/*
Finishes processing an HTTP request.
The default behavior is to keep the text edit read only.
If an error has occurred, the user interface is made available
to the user for further input, allowing a new fetch to be
started.
If the HTTP get request has finished, we make the
user interface available to the user for further input.
*/
void RSSListing::finished(QNetworkReply *reply)
{
Q_UNUSED(reply);
lineEdit->setReadOnly(false);
fetchButton->setEnabled(true);
}
/*
Parses the XML data and creates treeWidget items accordingly.
*/
void RSSListing::parseXml()
{
while (!xml.atEnd()) {
xml.readNext();
if (xml.isStartElement()) {
if (xml.name() == u"item")
linkString = xml.attributes().value("rss:about").toString();
currentTag = xml.name().toString();
} else if (xml.isEndElement()) {
if (xml.name() == u"item") {
QTreeWidgetItem *item = new QTreeWidgetItem;
item->setText(0, titleString);
item->setText(1, linkString);
treeWidget->addTopLevelItem(item);
titleString.clear();
linkString.clear();
}
} else if (xml.isCharacters() && !xml.isWhitespace()) {
if (currentTag == "title")
titleString += xml.text();
else if (currentTag == "link")
linkString += xml.text();
}
}
if (xml.error() && xml.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
qWarning() << "XML ERROR:" << xml.lineNumber() << ": " << xml.errorString();
}
}
/*
Open the link in the browser
*/
void RSSListing::itemActivated(QTreeWidgetItem * item)
{
QDesktopServices::openUrl(QUrl(item->text(1)));
}
void RSSListing::error(QNetworkReply::NetworkError)
{
qWarning("error retrieving RSS feed");
currentReply->disconnect(this);
currentReply->deleteLater();
currentReply = 0;
}

View File

@ -4,6 +4,10 @@
cmake_minimum_required(VERSION 3.16)
project(savegame LANGUAGES CXX)
if (ANDROID)
message(FATAL_ERROR "This project cannot be built on Android.")
endif()
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()

View File

@ -6,15 +6,10 @@
#include <QMetaEnum>
#include <QTextStream>
Character::Character()
= default;
Character::Character() = default;
Character::Character(const QString &name,
int level,
Character::ClassType classType) :
mName(name),
mLevel(level),
mClassType(classType)
Character::Character(const QString &name, int level, Character::ClassType classType)
: mName(name), mLevel(level), mClassType(classType)
{
}
@ -48,35 +43,41 @@ void Character::setClassType(Character::ClassType classType)
mClassType = classType;
}
//! [0]
void Character::read(const QJsonObject &json)
//! [fromJson]
Character Character::fromJson(const QJsonObject &json)
{
if (json.contains("name") && json["name"].isString())
mName = json["name"].toString();
Character result;
if (json.contains("level") && json["level"].isDouble())
mLevel = json["level"].toInt();
if (const QJsonValue v = json["name"]; v.isString())
result.mName = v.toString();
if (json.contains("classType") && json["classType"].isDouble())
mClassType = ClassType(json["classType"].toInt());
if (const QJsonValue v = json["level"]; v.isDouble())
result.mLevel = v.toInt();
if (const QJsonValue v = json["classType"]; v.isDouble())
result.mClassType = ClassType(v.toInt());
return result;
}
//! [0]
//! [fromJson]
//! [1]
void Character::write(QJsonObject &json) const
//! [toJson]
QJsonObject Character::toJson() const
{
QJsonObject json;
json["name"] = mName;
json["level"] = mLevel;
json["classType"] = mClassType;
return json;
}
//! [1]
//! [toJson]
void Character::print(int indentation) const
void Character::print(QTextStream &s, int indentation) const
{
const QString indent(indentation * 2, ' ');
QTextStream(stdout) << indent << "Name:\t" << mName << "\n";
QTextStream(stdout) << indent << "Level:\t" << mLevel << "\n";
const QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType);
QString className = QMetaEnum::fromType<ClassType>().valueToKey(mClassType);
QTextStream(stdout) << indent << "Class:\t" << className << "\n";
s << indent << "Name:\t" << mName << "\n"
<< indent << "Level:\t" << mLevel << "\n"
<< indent << "Class:\t" << className << "\n";
}

View File

@ -8,15 +8,15 @@
#include <QObject>
#include <QString>
QT_FORWARD_DECLARE_CLASS(QTextStream)
//! [0]
class Character
{
Q_GADGET
public:
enum ClassType {
Warrior, Mage, Archer
};
enum ClassType { Warrior, Mage, Archer };
Q_ENUM(ClassType)
Character();
@ -31,10 +31,11 @@ public:
ClassType classType() const;
void setClassType(ClassType classType);
void read(const QJsonObject &json);
void write(QJsonObject &json) const;
static Character fromJson(const QJsonObject &json);
QJsonObject toJson() const;
void print(QTextStream &s, int indentation = 0) const;
void print(int indentation = 0) const;
private:
QString mName;
int mLevel = 0;

View File

@ -3,7 +3,7 @@
/*!
\example serialization/savegame
\examplecategory {Input/Output}
\examplecategory {Data Processing & I/O}
\title JSON Save Game Example
\brief The JSON Save Game example demonstrates how to save and load a
@ -11,11 +11,12 @@
Many games provide save functionality, so that the player's progress through
the game can be saved and loaded at a later time. The process of saving a
game generally involves serializing each game object's member variables
to a file. Many formats can be used for this purpose, one of which is JSON.
With QJsonDocument, you also have the ability to serialize a document in a
\l {RFC 7049} {CBOR} format, which is great if you
don't want the save file to be readable, or if you need to keep the file size down.
game generally involves serializing each game object's member variables to a
file. Many formats can be used for this purpose, one of which is JSON. With
QJsonDocument, you also have the ability to serialize a document in a \l
{RFC 7049} {CBOR} format, which is great if you don't want the save file to
be easy to read (but see \l {Parsing and displaying CBOR data} for how it \e
can be read), or if you need to keep the file size down.
In this example, we'll demonstrate how to save and load a simple game to
and from JSON and binary formats.
@ -25,45 +26,83 @@
The Character class represents a non-player character (NPC) in our game, and
stores the player's name, level, and class type.
It provides read() and write() functions to serialise its member variables.
It provides static fromJson() and non-static toJson() functions to
serialise itself.
\note This pattern (fromJson()/toJson()) works because QJsonObjects can be
constructed independent of an owning QJsonDocument, and because the data
types being (de)serialized here are value types, so can be copied. When
serializing to another format — for example XML or QDataStream, which require passing
a document-like object — or when the object identity is important (QObject
subclasses, for example), other patterns may be more suitable. See the
\l{xml/dombookmarks} and \l{xml/streambookmarks} examples for XML, and the
implementation of \l QListWidgetItem::read() and \l QListWidgetItem::write()
for idiomatic QDataStream serialization. The \c{print()} functions in this example
are good examples of QTextStream serialization, even though they, of course, lack
the deserialization side.
\snippet serialization/savegame/character.h 0
Of particular interest to us are the read and write function
Of particular interest to us are the fromJson() and toJson() function
implementations:
\snippet serialization/savegame/character.cpp 0
\snippet serialization/savegame/character.cpp fromJson
In the read() function, we assign Character's members values from the
QJsonObject argument. You can use either \l QJsonObject::operator[]() or
QJsonObject::value() to access values within the JSON object; both are
const functions and return QJsonValue::Undefined if the key is invalid. We
check if the keys are valid before attempting to read them with
QJsonObject::contains().
In the fromJson() function, we construct a local \c result Character object
and assign \c{result}'s members values from the QJsonObject argument. You
can use either \l QJsonObject::operator[]() or QJsonObject::value() to
access values within the JSON object; both are const functions and return
QJsonValue::Undefined if the key is invalid. In particular, the \c{is...}
functions (for example \l QJsonValue::isString(), \l
QJsonValue::isDouble()) return \c false for QJsonValue::Undefined, so we
can check for existence as well as the correct type in a single lookup.
\snippet serialization/savegame/character.cpp 1
If a value does not exist in the JSON object, or has the wrong type, we
don't write to the corresponding \c result member, either, thereby
preserving any values the default constructor may have set. This means
default values are centrally defined in one location (the default
constructor) and need not be repeated in serialisation code
(\l{https://en.wikipedia.org/wiki/Don%27t_repeat_yourself}{DRY}).
In the write() function, we do the reverse of the read() function; assign
values from the Character object to the JSON object. As with accessing
values, there are two ways to set values on a QJsonObject:
\l QJsonObject::operator[]() and QJsonObject::insert(). Both will override
any existing value at the given key.
Observe the use of
\l{https://en.cppreference.com/w/cpp/language/if#If_statements_with_initializer}
{C++17 if-with-initializer} to separate scoping and checking of the variable \c v.
This means we can keep the variable name short, because its scope is limited.
Next up is the Level class:
Compare that to the naïve approach using \c QJsonObject::contains():
\badcode
if (json.contains("name") && json["name"].isString())
result.mName = json["name"].toString();
\endcode
which, beside being less readable, requires a total of three lookups (no,
the compiler will \e not optimize these into one), so is three times
slower and repeats \c{"name"} three times (violating the DRY principle).
\snippet serialization/savegame/character.cpp toJson
In the toJson() function, we do the reverse of the fromJson() function;
assign values from the Character object to a new JSON object we then
return. As with accessing values, there are two ways to set values on a
QJsonObject: \l QJsonObject::operator[]() and \l QJsonObject::insert().
Both will override any existing value at the given key.
\section1 The Level Class
\snippet serialization/savegame/level.h 0
We want to have several levels in our game, each with several NPCs, so we
keep a QList of Character objects. We also provide the familiar read() and
write() functions.
We want the levels in our game to each each have several NPCs, so we keep a QList
of Character objects. We also provide the familiar fromJson() and toJson()
functions.
\snippet serialization/savegame/level.cpp 0
\snippet serialization/savegame/level.cpp fromJson
Containers can be written and read to and from JSON using QJsonArray. In our
Containers can be written to and read from JSON using QJsonArray. In our
case, we construct a QJsonArray from the value associated with the key
\c "npcs". Then, for each QJsonValue element in the array, we call
toObject() to get the Character's JSON object. The Character object can then
read their JSON and be appended to our NPC array.
toObject() to get the Character's JSON object. Character::fromJson() can
then turn that QJSonObject into a Character object to append to our NPC array.
\note \l{Container Classes}{Associate containers} can be written by storing
the key in each value object (if it's not already). With this approach, the
@ -71,11 +110,13 @@
element is used as the key to construct the container when reading it back
in.
\snippet serialization/savegame/level.cpp 1
\snippet serialization/savegame/level.cpp toJson
Again, the write() function is similar to the read() function, except
Again, the toJson() function is similar to the fromJson() function, except
reversed.
\section1 The Game Class
Having established the Character and Level classes, we can move on to
the Game class:
@ -87,26 +128,43 @@
Next, we provide accessors for the player and levels. We then expose three
functions: newGame(), saveGame() and loadGame().
The read() and write() functions are used by saveGame() and loadGame().
The read() and toJson() functions are used by saveGame() and loadGame().
\snippet serialization/savegame/game.cpp 0
\div{class="admonition note"}\b{Note:}
Despite \c Game being a value class, we assume that the author wants a game to have
identity, much like your main window would have. We therefore don't use a
static fromJson() function, which would create a new object, but a read()
function we can call on existing objects. There's a 1:1 correspondence
between read() and fromJson(), in that one can be implemented in terms of
the other:
\code
void read(const QJsonObject &json) { *this = fromJson(json); }
static Game fromObject(const QJsonObject &json) { Game g; g.read(json); return g; }
\endcode
We just use what's more convenient for callers of the functions.
\enddiv
\snippet serialization/savegame/game.cpp newGame
To setup a new game, we create the player and populate the levels and their
NPCs.
\snippet serialization/savegame/game.cpp 1
\snippet serialization/savegame/game.cpp read
The first thing we do in the read() function is tell the player to read
itself. We then clear the level array so that calling loadGame() on the
same Game object twice doesn't result in old levels hanging around.
The read() function starts by replacing the player with the
one read from JSON. We then clear() the level array so that calling
loadGame() on the same Game object twice doesn't result in old levels
hanging around.
We then populate the level array by reading each Level from a QJsonArray.
\snippet serialization/savegame/game.cpp 2
\snippet serialization/savegame/game.cpp toJson
We write the game to JSON similarly to how we write Level.
Writing the game to JSON is similar to writing a level.
\snippet serialization/savegame/game.cpp 3
\snippet serialization/savegame/game.cpp loadGame
When loading a saved game in loadGame(), the first thing we do is open the
save file based on which format it was saved to; \c "save.json" for JSON,
@ -120,14 +178,16 @@
After constructing the QJsonDocument, we instruct the Game object to read
itself and then return \c true to indicate success.
\snippet serialization/savegame/game.cpp 4
\snippet serialization/savegame/game.cpp saveGame
Not surprisingly, saveGame() looks very much like loadGame(). We determine
the file extension based on the format, print a warning and return \c false
if the opening of the file fails. We then write the Game object to a
QJsonDocument, and call either QJsonDocument::toJson() or to
QJsonDocument::toBinaryData() to save the game, depending on which format
was specified.
QJsonObject. To save the game in the format that was specified, we
convert the JSON object into either a QJsonDocument for a subsequent
QJsonDocument::toJson() call, or a QCborValue for QCborValue::toCbor().
\section1 Tying It All Together
We are now ready to enter main():

Some files were not shown because too many files have changed in this diff Show More