Revision control

Copy as Markdown

# Copyright (C) 2026 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Compiles Java sources into a JAR using Gradle.
#
# Each module provides its own Gradle project (build.gradle, settings.gradle, etc.).
# Gradle is invoked from the source tree while the build output is redirected
# to the CMake build tree. Gradle builds an AAR and from that a JAR is extracted.
#
# The property 'isQtCMakeBuild' is always set to "true" to distinguish CMake builds
# from IDE builds where modules can depend on each other without affecting the CMake
# build which treats modules as separate and use JAR includes. It can be used as:
# if (!findProperty('isQtCMakeBuild')) {
# dependencies {
# compileOnly 'org.qtproject.qt:Qt6Android'
# }
# }
#
# Usage:
# qt_internal_add_jar(<target>
# SOURCES src1.java src2.java
# [INCLUDE_JARS jar1.jar jar2.jar]
# [OUTPUT_DIR <dir>] : defaults to Qt install jar dir
# [GRADLE_PROJECT_DIR <dir>] : defaults to CMAKE_CURRENT_SOURCE_DIR
# )
# Maps CMAKE_BUILD_TYPE to the Gradle build variant ('Debug' or 'Release').
function(_qt_internal_resolve_gradle_build_variant out_var)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(${out_var} "Debug" PARENT_SCOPE)
else()
set(${out_var} "Release" PARENT_SCOPE)
endif()
endfunction()
function(qt_internal_add_jar target)
set(options)
set(oneValueArgs OUTPUT_DIR GRADLE_PROJECT_DIR)
set(multiValueArgs INCLUDE_JARS SOURCES)
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Valid Gradle project files
if(arg_GRADLE_PROJECT_DIR)
get_filename_component(gradle_source_dir "${arg_GRADLE_PROJECT_DIR}" ABSOLUTE)
else()
set(gradle_source_dir "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
# Ease the switching phase, check for more than build.gradle because some modules
# have it already for IDE integration, but it's not the same one for JAR builds.
if(NOT EXISTS "${gradle_source_dir}/build.gradle"
OR NOT EXISTS "${gradle_source_dir}/settings.gradle"
OR NOT EXISTS "${gradle_source_dir}/gradle/libs.versions.toml")
if(QT_FEATURE_developer_build)
message(WARNING
"Missing Gradle files at '${gradle_source_dir}' for ${target} build."
"Create Gradle build files (build.gradle, settings.gradle, libs.versions.toml)"
"or if those files exist elsewhere than the current target's source dir,"
"set GRADLE_PROJECT_DIR to that path. Falling back to javac.")
endif()
_qt_internal_add_jar_with_javac("${target}" ${ARGN})
return()
endif()
message(STATUS "Checking Gradle offline cache for ${target} JAR")
_qt_internal_verify_gradle_offline_cache("${gradle_source_dir}")
# Java source and target versions
set(javac_target_version "${QT_ANDROID_JAVAC_TARGET}")
if (NOT javac_target_version)
set(javac_target_version "17")
endif()
set(javac_source_version "${QT_ANDROID_JAVAC_SOURCE}")
if (NOT javac_source_version)
set(javac_source_version "17")
endif()
# Android SDK versions
set(compile_sdk "${QT_ANDROID_API_USED_FOR_JAVA}")
string(REGEX REPLACE "^android-" "" compile_sdk "${compile_sdk}")
string(REGEX REPLACE "\\.[0-9]+$" "" compile_sdk "${compile_sdk}")
set(min_sdk "${ANDROID_PLATFORM}")
string(REGEX REPLACE "^android-" "" min_sdk "${min_sdk}")
# Evaluate common paths
if(PROJECT_NAME STREQUAL "QtBase" OR QT_SUPERBUILD)
set(qt_jar_dir "${QT_BUILD_DIR}/jar")
else()
set(qt_data_dir ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_DATA})
set(qt_jar_dir "${qt_data_dir}/jar")
endif()
# Output file and directory
set(jar_output_dir "${arg_OUTPUT_DIR}")
if(NOT jar_output_dir)
set(jar_output_dir "${qt_jar_dir}")
endif()
file(TO_CMAKE_PATH "${jar_output_dir}" jar_output_dir)
set(jar_output_file "${jar_output_dir}/${target}.jar")
# Collect source paths
set(absolute_sources "")
foreach(path IN LISTS arg_SOURCES)
get_filename_component(absolute_path "${path}" ABSOLUTE)
list(APPEND absolute_sources "${absolute_path}")
endforeach()
set_property(DIRECTORY APPEND PROPERTY _qt_jar_sources "${absolute_sources}")
# Locate the Gradle wrapper
_qt_internal_android_gradle_template_dir(gradle_template_dir)
_qt_internal_android_gradlew_name(gradlew_name)
set(gradlew "${gradle_template_dir}/${gradlew_name}")
# Build directory in the build tree to keeps source tree clean
set(gradle_build_dir "${CMAKE_CURRENT_BINARY_DIR}/${target}_gradle")
file(TO_CMAKE_PATH "${gradle_build_dir}" gradle_build_dir)
_qt_internal_resolve_gradle_build_variant(build_variant)
# Gradle command line properties
set(gradle_props
"-PbuildDir=${gradle_build_dir}/build"
"-PjarOutputDir=${jar_output_dir}"
"-PbuildVariant=${build_variant}"
"-PjavacSource=${javac_source_version}"
"-PjavacTarget=${javac_target_version}"
"-PcompileSdk=${compile_sdk}"
"-PminSdk=${min_sdk}"
"-PisQtCMakeBuild=true"
)
# Include JARs (comma-separated)
set(include_jars_csv "")
foreach(jar IN LISTS arg_INCLUDE_JARS)
file(TO_CMAKE_PATH "${jar}" jar_path)
if(include_jars_csv)
string(APPEND include_jars_csv ",")
endif()
string(APPEND include_jars_csv "${jar_path}")
endforeach()
set(gradle_extra_args "")
if(include_jars_csv)
list(APPEND gradle_extra_args "-PincludeJars=${include_jars_csv}")
endif()
if(CMAKE_HOST_WIN32)
# On Windows, run Gradle with --no-daemon to avoid CI cleanup issues.
list(APPEND gradle_extra_args "--no-daemon")
endif()
if(NOT QT_ALLOW_DOWNLOAD)
list(APPEND gradle_extra_args "--offline")
endif()
# Build tools version
_qt_internal_android_get_sdk_build_tools_revision(build_tools_revision)
list(APPEND gradle_props "-PbuildToolsVersion=${build_tools_revision}")
# Invoke Gradle
add_custom_command(OUTPUT "${jar_output_file}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${jar_output_dir}"
COMMAND ${CMAKE_COMMAND} -E env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}"
"${gradlew}"
--init-script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/android/qt-android-init.gradle"
--project-cache-dir "${gradle_build_dir}/.gradle"
--no-configuration-cache
${gradle_extra_args}
${gradle_props}
${target}Jar
WORKING_DIRECTORY "${gradle_source_dir}"
DEPENDS
"${gradle_source_dir}/build.gradle"
"${gradle_source_dir}/settings.gradle"
${absolute_sources}
${arg_INCLUDE_JARS}
COMMENT "Building ${target}.jar with Gradle"
VERBATIM
)
add_custom_target(${target} ALL DEPENDS "${jar_output_file}")
# Expose source files to IDEs
foreach(path IN LISTS arg_SOURCES)
_qt_internal_expose_source_file_to_ide(${target} "${path}")
endforeach()
# Target properties for install step with CMake's install_jar()
set_target_properties(${target} PROPERTIES
JAR_FILE "${jar_output_file}"
INSTALL_FILES "${jar_output_file}"
)
endfunction()
# Fallback: builds the JAR using javac via CMake's add_jar().
function(_qt_internal_add_jar_with_javac 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)
set(javac_target_version "17")
endif()
set(javac_source_version "${QT_ANDROID_JAVAC_SOURCE}")
if (NOT javac_source_version)
set(javac_source_version "17")
endif()
set(CMAKE_JAVA_COMPILE_FLAGS -source "${javac_source_version}" -target "${javac_target_version}"
-Xlint:all -Xdoclint:all,-missing -classpath "${QT_ANDROID_JAR}"
)
set(absolute_sources "")
foreach(path IN LISTS arg_SOURCES)
get_filename_component(absolute_path "${path}" ABSOLUTE)
list(APPEND absolute_sources "${absolute_path}")
endforeach()
set_property(DIRECTORY APPEND PROPERTY _qt_jar_sources "${absolute_sources}")
add_jar(${target}
SOURCES ${absolute_sources}
INCLUDE_JARS ${arg_INCLUDE_JARS}
OUTPUT_DIR ${arg_OUTPUT_DIR}
)
foreach(f IN LISTS arg_SOURCES)
_qt_internal_expose_source_file_to_ide(${target} "${f}")
endforeach()
endfunction()
# Verifies that a Gradle project's dependencies are cached for offline builds.
function(_qt_internal_verify_gradle_offline_cache gradle_source_dir)
if(QT_ALLOW_DOWNLOAD)
return()
endif()
set(setup_script "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/QtSetupAndroid.cmake")
if(NOT EXISTS "${setup_script}")
message(WARNING "Cannot find ${setup_script}, skipping the Gradle offline cache check.")
return()
endif()
if(PROJECT_NAME STREQUAL "QtBase" OR QT_SUPERBUILD)
set(qt_dir "${QtBase_SOURCE_DIR}")
else()
set(qt_dir "${QT6_INSTALL_PREFIX}")
endif()
set(action_resolve_arg "-DACTION_RESOLVE_GRADLE=ON")
set(qt_dir_arg "-DQT_ROOT_DIR=${qt_dir}")
set(project_dir_arg "-DGRADLE_PROJECT_DIR=${gradle_source_dir}")
_qt_internal_resolve_gradle_build_variant(build_variant)
set(build_variant_arg "-DGRADLE_BUILD_VARIANT=${build_variant}")
set(only_verify_arg "-DONLY_VERIFY_GRADLE_CACHE=ON")
_qt_internal_android_get_sdk_build_tools_revision(build_tools_revision)
if(NOT IS_DIRECTORY "${ANDROID_SDK_ROOT}/build-tools/${build_tools_revision}")
message(FATAL_ERROR "Android build-tools '${build_tools_revision}' is not installed.")
endif()
set(build_tools_revision_arg "-DQT_ANDROID_SDK_BUILD_TOOLS_REVISION=${build_tools_revision}")
set(gradle_env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}")
execute_process(
COMMAND "${CMAKE_COMMAND}" -E env "${gradle_env}"
"${CMAKE_COMMAND}"
"${action_resolve_arg}"
"${qt_dir_arg}"
"${project_dir_arg}"
"${build_variant_arg}"
"${only_verify_arg}"
"${build_tools_revision_arg}"
-P "${setup_script}"
RESULT_VARIABLE verify_result
)
if(NOT verify_result EQUAL 0)
string(JOIN " " resolve_cmd_args
"${action_resolve_arg}" "${qt_dir_arg}" "${project_dir_arg}"
"${build_variant_arg}" "${build_tools_revision_arg}")
message(FATAL_ERROR
"The offline Gradle check failed for the JAR build under '${gradle_source_dir}'.\n"
"Downloads are disabled when building Qt and Gradle dependencies haven't been cached "
"for such offline builds. Run the following command to populate the Gradle cache:\n"
" cmake -E env ${gradle_env} cmake ${resolve_cmd_args} -P ${setup_script}\n"
"Or configure Qt with QT_ALLOW_DOWNLOAD=ON to allow downloads at build time.\n"
"Check the Gradle output above for more information."
)
endif()
endfunction()