6.5.3 clean
@ -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
@ -1,2 +0,0 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
@ -1 +1 @@
|
||||
QT_PACKAGEDATE_STR=2023-05-22
|
||||
QT_PACKAGEDATE_STR=2023-09-25
|
2
.tag
@ -1 +1 @@
|
||||
55aee8697512af105dfefabc1e2ec41d4df1e45e
|
||||
372eaedc5b8c771c46acc4c96e91bbade4ca3624
|
||||
|
@ -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}")
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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}"
|
||||
|
@ -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".
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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")
|
||||
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
")
|
||||
|
@ -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)
|
||||
|
@ -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})
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
300
conanfile.py
@ -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
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
)
|
||||
|
1
doc/config/exampleurl-qtpositioning.qdocconf
Normal file
@ -0,0 +1 @@
|
||||
url.examples = "https://code.qt.io/cgit/qt/qtpositioning.git/tree/examples/\1?h=$QT_VER"
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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 = \
|
||||
|
@ -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"
|
||||
|
@ -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" \
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
1
doc/global/template/style/tech_preview.svg
Normal 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 |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 222 KiB |
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 19 KiB |
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
89
examples/corelib/serialization/convert/debugtextdumper.cpp
Normal 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;
|
||||
}
|
23
examples/corelib/serialization/convert/debugtextdumper.h
Normal 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
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
@ -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()
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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():
|
||||
|
||||
|