qt5base-lts/cmake/QtBuildInformation.cmake
Alexandru Croitor bf315c5526 CMake: Make build system of installed Qt more relocatable
Aka handle CMAKE_INSTALL_PREFIX in a more relocatable way.

The following story inspired this change.

If a user wants to build a Qt repo into a different install prefix
than the usual Qt one, this will fail configuration because we
look for various things like syncqt, qdoc, etc relative to
CMAKE_INSTALL_PREFIX, which will now point to a different location
where none of the above tools are located.

The intent for such a use case is to support building Qt packages with
Conan, which sets a random install prefix when configuring a repo.

The idea is to derive the qt prefix dynamically from the
QtBuildInternals package location. Essentially it's a reverse relative
path from the QtBuildInternalsConfig.cmake file to the install prefix
that was specified when initially configuring qtbase.

Once the dynamic prefix is computed (so we know where the possibly
relocated Qt is), we can find tools like syncqt and qdoc.

This is an initial attempt to support a use case like that.

More design work will probably needed in case if tools / libs need to
be found in a location different than the Qt install prefix (so
support for multiple install prefixes / search paths).

An example of such a case would be when building qtdeclarative and
qtquickcontrols2 as Conan packages in one go. Most likely the
qmltyperegistrar tool will be located in the random install prefix
set by Conan, so building qtquickcontrols2 might fail due to not
finding the tool in the original Qt install prefix.

As to the implementation details, the change does the following:
- Dynamically computes and sets the
  QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX variable when
  find_package()'ing QtBuildInternals. It's an absolute path
  pointing to where the relocated Qt is.

- When building qtbase this variable is not yet available (due
  to QtBuildInternalsExtra not existing), in that case we set
  the variable to the absolute path of CMAKE_INSTALL_PREFIX
  (but only for the initial qtbase configuration).

- Remove QT_BUILD_INTERNALS_ORIGINAL_INSTALL_PREFIX which was used
  for standalone tests purposes. It's not needed now that we compute
  the location of the Qt prefix dynamically.

- The Unixy qt-cmake and qt-cmake-private shell scripts now
  use a relative path to find the toolchain file we created.

- The toolchain file also dynamically computes the location of the Qt
  packages, and adds them to CMAKE_PREFIX_PATH.

- A lot of existing CMAKE_INSTALL_PREFIX uses are replaced with
  QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX. This includes finding
  tool locations, mkspecs dir, path environment setup for tools, etc.

- Some places still use CMAKE_PREFIX_PATH in the following cases
  - When determining paths while configuring qtbase (valid cases)
  - When I wasn't sure what the behavior should be, so I left them
    as-is (an example is documentation generation, do we want to
    install it into the random Conan prefix, or into the main prefix?
    Currently it installs in the random prefix).

Note that relocating a Qt installation does not work for non-prefix /
non-installed builds, due to hardcoded paths to include directories
and libraries in generated FooTargets.cmake files.

Task-number: QTBUG-83999
Change-Id: I87d6558729db93121b1715771034b03ce3295923
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
2020-05-07 15:41:16 +02:00

375 lines
14 KiB
CMake

function(qt_print_feature_summary)
include(FeatureSummary)
# Show which packages were found.
feature_summary(WHAT PACKAGES_FOUND
REQUIRED_PACKAGES_NOT_FOUND
RECOMMENDED_PACKAGES_NOT_FOUND
OPTIONAL_PACKAGES_NOT_FOUND
RUNTIME_PACKAGES_NOT_FOUND
FATAL_ON_MISSING_REQUIRED_PACKAGES)
qt_configure_print_summary()
endfunction()
function(qt_print_build_instructions)
if((NOT PROJECT_NAME STREQUAL "QtBase" AND
NOT PROJECT_NAME STREQUAL "Qt") OR
QT_BUILD_STANDALONE_TESTS)
return()
endif()
set(build_command "cmake --build . --parallel")
set(install_command "cmake --install .")
message("Qt is now configured for building. Just run '${build_command}'.")
if(QT_WILL_INSTALL)
message("Once everything is built, you must run '${install_command}'.")
message("Qt will be installed into '${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}'")
else()
message("Once everything is built, Qt is installed.")
message("You should NOT run '${install_command}'")
message("Note that this build cannot be deployed to other machines or devices.")
endif()
message("To configure and build other modules, you can use the following convenience script:
${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_BINDIR}/qt-cmake")
message("\nIf reconfiguration fails for some reason, try to remove 'CMakeCache.txt' \
from the build directory \n")
endfunction()
function(qt_configure_print_summary)
# Evaluate all recorded commands.
qt_configure_eval_commands()
# Show Qt-specific configure summary and any notes, wranings, etc.
if(__qt_configure_reports)
message("Configure summary:\n${__qt_configure_reports}")
endif()
if(__qt_configure_notes)
message("${__qt_configure_notes}")
endif()
if(__qt_configure_warnings)
message("${__qt_configure_warnings}")
endif()
if(__qt_configure_errors)
message("${__qt_configure_errors}")
endif()
message("")
if(__qt_configure_an_error_occurred)
message(FATAL_ERROR "Check the configuration messages for an error that has occurred.")
endif()
endfunction()
# Takes a list of arguments, and saves them to be evaluated at the end of the configuration
# phase when the configuration summary is shown.
function(qt_configure_record_command)
# Don't record commands when only evaluating features of a configure.cmake file.
if(__QtFeature_only_evaluate_features)
return()
endif()
get_property(command_count GLOBAL PROPERTY qt_configure_command_count)
if(NOT DEFINED command_count)
set(command_count 0)
else()
math(EXPR command_count "${command_count}+1")
endif()
set_property(GLOBAL PROPERTY qt_configure_command_${command_count} "${ARGV}")
set_property(GLOBAL PROPERTY qt_configure_command_count "${command_count}")
endfunction()
function(qt_configure_eval_commands)
get_property(command_count GLOBAL PROPERTY qt_configure_command_count)
if(NOT command_count)
set(command_count 0)
endif()
set(command_index 0)
while(command_index LESS command_count)
get_property(command_args GLOBAL PROPERTY qt_configure_command_${command_index})
if(NOT command_args)
message(FATAL_ERROR
"Empty arguments encountered while processing qt configure reports.")
endif()
list(POP_FRONT command_args command_name)
if(command_name STREQUAL ADD_SUMMARY_SECTION)
qt_configure_process_add_summary_section(${command_args})
elseif(command_name STREQUAL END_SUMMARY_SECTION)
qt_configure_process_end_summary_section(${command_args})
elseif(command_name STREQUAL ADD_REPORT_ENTRY)
qt_configure_process_add_report_entry(${command_args})
elseif(command_name STREQUAL ADD_SUMMARY_ENTRY)
qt_configure_process_add_summary_entry(${command_args})
elseif(command_name STREQUAL ADD_BUILD_TYPE_AND_CONFIG)
qt_configure_process_add_summary_build_type_and_config(${command_args})
elseif(command_name STREQUAL ADD_BUILD_MODE)
qt_configure_process_add_summary_build_mode(${command_args})
elseif(command_name STREQUAL ADD_BUILD_PARTS)
qt_configure_process_add_summary_build_parts(${command_args})
endif()
math(EXPR command_index "${command_index}+1")
endwhile()
# Propagate content to parent.
set(__qt_configure_reports "${__qt_configure_reports}" PARENT_SCOPE)
set(__qt_configure_notes "${__qt_configure_notes}" PARENT_SCOPE)
set(__qt_configure_warnings "${__qt_configure_warnings}" PARENT_SCOPE)
set(__qt_configure_errors "${__qt_configure_errors}" PARENT_SCOPE)
set(__qt_configure_an_error_occurred "${__qt_configure_an_error_occurred}" PARENT_SCOPE)
endfunction()
macro(qt_configure_add_report message)
string(APPEND __qt_configure_reports "\n${message}")
set(__qt_configure_reports "${__qt_configure_reports}" PARENT_SCOPE)
endmacro()
macro(qt_configure_add_report_padded label message)
qt_configure_get_padded_string("${__qt_configure_indent}${label}" "${message}" padded_message)
string(APPEND __qt_configure_reports "\n${padded_message}")
set(__qt_configure_reports "${__qt_configure_reports}" PARENT_SCOPE)
endmacro()
function(qt_configure_get_padded_string label value out_var)
set(pad_string "........................................")
string(LENGTH "${label}" label_len)
string(LENGTH "${pad_string}" pad_len)
math(EXPR pad_len "${pad_len}-${label_len}")
if(pad_len LESS "0")
set(pad_len "0")
endif()
string(SUBSTRING "${pad_string}" 0 "${pad_len}" pad_string)
set(output "${label} ${pad_string} ${value}")
set("${out_var}" "${output}" PARENT_SCOPE)
endfunction()
function(qt_configure_add_summary_entry)
qt_configure_record_command(ADD_SUMMARY_ENTRY ${ARGV})
endfunction()
function(qt_configure_process_add_summary_entry)
qt_parse_all_arguments(arg "qt_configure_add_summary_entry"
""
"ARGS;TYPE;MESSAGE" "CONDITION" ${ARGN})
if(NOT arg_TYPE)
set(arg_TYPE "feature")
endif()
if(NOT "${arg_CONDITION}" STREQUAL "")
qt_evaluate_config_expression(condition_result ${arg_CONDITION})
if(NOT condition_result)
return()
endif()
endif()
if(arg_TYPE STREQUAL "firstAvailableFeature")
set(first_feature_found FALSE)
set(message "")
string(REPLACE " " ";" args_list "${arg_ARGS}")
foreach(feature ${args_list})
qt_feature_normalize_name("${feature}" feature)
if(NOT DEFINED QT_FEATURE_${feature})
message(FATAL_ERROR "Asking for a report on undefined feature ${feature}.")
endif()
if(QT_FEATURE_${feature})
set(first_feature_found TRUE)
set(message "${QT_FEATURE_LABEL_${feature}}")
break()
endif()
endforeach()
if(NOT first_feature_found)
set(message "<none>")
endif()
qt_configure_add_report_padded("${arg_MESSAGE}" "${message}")
elseif(arg_TYPE STREQUAL "featureList")
set(available_features "")
string(REPLACE " " ";" args_list "${arg_ARGS}")
foreach(feature ${args_list})
qt_feature_normalize_name("${feature}" feature)
if(NOT DEFINED QT_FEATURE_${feature})
message(FATAL_ERROR "Asking for a report on undefined feature ${feature}.")
endif()
if(QT_FEATURE_${feature})
list(APPEND available_features "${QT_FEATURE_LABEL_${feature}}")
endif()
endforeach()
if(NOT available_features)
set(message "<none>")
else()
list(JOIN available_features " " message)
endif()
qt_configure_add_report_padded("${arg_MESSAGE}" "${message}")
elseif(arg_TYPE STREQUAL "feature")
qt_feature_normalize_name("${arg_ARGS}" feature)
set(label "${QT_FEATURE_LABEL_${feature}}")
if(NOT label)
set(label "${feature}")
endif()
if(QT_FEATURE_${feature})
set(value "yes")
else()
set(value "no")
endif()
qt_configure_add_report_padded("${label}" "${value}")
endif()
endfunction()
function(qt_configure_add_summary_build_type_and_config)
qt_configure_record_command(ADD_BUILD_TYPE_AND_CONFIG ${ARGV})
endfunction()
function(qt_configure_process_add_summary_build_type_and_config)
get_property(subarch_summary GLOBAL PROPERTY qt_configure_subarch_summary)
set(message
"Building for: ${QT_QMAKE_TARGET_MKSPEC} (${TEST_architecture_arch}, CPU features: ${subarch_summary})")
qt_configure_add_report("${message}")
set(message "Compiler: ")
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
string(APPEND message "clang (Apple)")
elseif(CLANG)
string(APPEND message "clang")
elseif(ICC)
string(APPEND message "intel_icc")
elseif(QCC)
string(APPEND message "rim_qcc")
elseif(GCC)
string(APPEND message "gcc")
elseif(MSVC)
string(APPEND message "msvc")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GHS")
string(APPEND message "ghs")
else()
string(APPEND message "unknown (${CMAKE_CXX_COMPILER_ID})")
endif()
string(APPEND message " ${CMAKE_CXX_COMPILER_VERSION}")
qt_configure_add_report("${message}")
endfunction()
function(qt_configure_add_summary_build_mode)
qt_configure_record_command(ADD_BUILD_MODE ${ARGV})
endfunction()
function(qt_configure_process_add_summary_build_mode label)
set(message "")
if(DEFINED CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
string(APPEND message "debug")
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
string(APPEND message "release")
elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
string(APPEND message "release (with debug info)")
else()
string(APPEND message "${CMAKE_BUILD_TYPE}")
endif()
elseif(DEFINED CMAKE_CONFIGURATION_TYPES)
if("Release" IN_LIST CMAKE_CONFIGURATION_TYPES
AND "Debug" IN_LIST CMAKE_CONFIGURATION_TYPES)
string(APPEND message "debug and release")
elseif("RelWithDebInfo" IN_LIST CMAKE_CONFIGURATION_TYPES
AND "Debug" IN_LIST CMAKE_CONFIGURATION_TYPES)
string(APPEND message "debug and release (with debug info)")
else()
string(APPEND message "${CMAKE_CONFIGURATION_TYPES}")
endif()
endif()
qt_configure_add_report_padded("${label}" "${message}")
endfunction()
function(qt_configure_add_summary_build_parts)
qt_configure_record_command(ADD_BUILD_PARTS ${ARGV})
endfunction()
function(qt_configure_process_add_summary_build_parts label)
qt_get_build_parts(parts)
string(REPLACE ";" " " message "${parts}")
qt_configure_add_report_padded("${label}" "${message}")
endfunction()
function(qt_configure_add_summary_section)
qt_configure_record_command(ADD_SUMMARY_SECTION ${ARGV})
endfunction()
function(qt_configure_process_add_summary_section)
qt_parse_all_arguments(arg "qt_configure_add_summary_section"
"" "NAME" "" ${ARGN})
qt_configure_add_report("${__qt_configure_indent}${arg_NAME}:")
if(NOT DEFINED __qt_configure_indent)
set(__qt_configure_indent " " PARENT_SCOPE)
else()
set(__qt_configure_indent "${__qt_configure_indent} " PARENT_SCOPE)
endif()
endfunction()
function(qt_configure_end_summary_section)
qt_configure_record_command(END_SUMMARY_SECTION ${ARGV})
endfunction()
function(qt_configure_process_end_summary_section)
string(LENGTH "${__qt_configure_indent}" indent_len)
if(indent_len GREATER_EQUAL 2)
string(SUBSTRING "${__qt_configure_indent}" 2 -1 __qt_configure_indent)
set(__qt_configure_indent "${__qt_configure_indent}" PARENT_SCOPE)
endif()
endfunction()
function(qt_configure_add_report_entry)
qt_configure_record_command(ADD_REPORT_ENTRY ${ARGV})
endfunction()
function(qt_configure_process_add_report_entry)
qt_parse_all_arguments(arg "qt_configure_add_report_entry"
""
"TYPE;MESSAGE" "CONDITION" ${ARGN})
set(possible_types NOTE WARNING ERROR FATAL_ERROR)
if(NOT "${arg_TYPE}" IN_LIST possible_types)
message(FATAL_ERROR "qt_configure_add_report_entry: '${arg_TYPE}' is not a valid type.")
endif()
if(NOT arg_MESSAGE)
message(FATAL_ERROR "qt_configure_add_report_entry: Empty message given.")
endif()
if(arg_TYPE STREQUAL "NOTE")
set(contents_var "__qt_configure_notes")
set(prefix "Note: ")
elseif(arg_TYPE STREQUAL "WARNING")
set(contents_var "__qt_configure_warnings")
set(prefix "WARNING: ")
elseif(arg_TYPE STREQUAL "ERROR")
set(contents_var "__qt_configure_errors")
set(prefix "ERROR: ")
elseif(arg_TYPE STREQUAL "FATAL_ERROR")
set(contents_var "__qt_configure_errors")
set(prefix "FATAL ERROR: ")
endif()
if(NOT "${arg_CONDITION}" STREQUAL "")
qt_evaluate_config_expression(condition_result ${arg_CONDITION})
endif()
if("${arg_CONDITION}" STREQUAL "" OR condition_result)
set(new_report "${prefix}${arg_MESSAGE}")
string(APPEND "${contents_var}" "\n${new_report}")
if(arg_TYPE STREQUAL "ERROR" OR arg_TYPE STREQUAL "FATAL_ERROR")
set(__qt_configure_an_error_occurred "TRUE" PARENT_SCOPE)
endif()
endif()
set("${contents_var}" "${${contents_var}}" PARENT_SCOPE)
endfunction()