diff options
Diffstat (limited to 'cmake/QtIRParsingHelpers.cmake')
-rw-r--r-- | cmake/QtIRParsingHelpers.cmake | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/cmake/QtIRParsingHelpers.cmake b/cmake/QtIRParsingHelpers.cmake new file mode 100644 index 00000000..d7d3f20e --- /dev/null +++ b/cmake/QtIRParsingHelpers.cmake @@ -0,0 +1,237 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Retrieves the contents of either .git/config or .gitmodules files for further parsing. +function(qt_ir_get_git_config_contents out_var) + set(options + READ_GITMODULES + READ_GIT_CONFIG + READ_GIT_CONFIG_LOCAL + ) + set(oneValueArgs + WORKING_DIRECTORY + ) + set(multiValueArgs "") + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(arg_READ_GITMODULES) + set(args -f .gitmodules) + set(file_message ".gitmodules") + elseif(arg_READ_GIT_CONFIG) + set(args "") + set(file_message ".git/config") + elseif(arg_READ_GIT_CONFIG_LOCAL) + set(args "--local") + set(file_message ".local .git/config") + else() + message(FATAL_ERROR "qt_ir_get_git_config_contents: No option specified") + endif() + + qt_ir_get_working_directory_from_arg(working_directory) + + qt_ir_execute_process_and_log_and_handle_error( + FORCE_QUIET + COMMAND_ARGS git config --list ${args} + OUT_OUTPUT_VAR git_output + WORKING_DIRECTORY "${working_directory}" + ERROR_MESSAGE "Failed to get ${file_message} contents for parsing") + + string(STRIP "${git_output}" git_output) + set(${out_var} "${git_output}" PARENT_SCOPE) +endfunction() + +# Checks whether the given url has a scheme like https:// or is just a +# relative path. +function(qt_ir_has_url_scheme url out_var) + string(REGEX MATCH "^[a-z][a-z0-9+\-.]*://" has_url_scheme "${url}") + + if(has_url_scheme) + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Parses a key-value line from a .git/config or .gitmodules file +macro(qt_ir_parse_git_key_value) + string(REGEX REPLACE "^submodule\\.([^.=]+)\\.([^.=]+)=(.*)$" "\\1;\\2;\\3" + parsed_line "${line}") + + list(LENGTH parsed_line parsed_line_length) + set(submodule_name "") + set(key "") + set(value "") + if(parsed_line_length EQUAL 3) + list(GET parsed_line 0 submodule_name) + list(GET parsed_line 1 key) + list(GET parsed_line 2 value) + endif() +endmacro() + +# Parses a url line from a .gitmodules file +# e.g. line - 'submodule.qtbase.url=../qtbase.git' +# +# Arguments +# +# submodule_name +# submodule name, the key in 'submodule.${submodule_name}.url' +# e.g. 'qtbase' +# url_value +# the url where to clone a repo from +# in perl script it was called $base +# e.g. '../qtbase.git', 'https://code.qt.io/playground/qlitehtml.git' +# parent_repo_base_git_path +# the base git path of the parent of the submodule +# it is either a relative dir or a full url +# in the perl script it was called $my_repo_base, +# it was passed as first arg to git_clone_all_submodules, +# it was passed the value of $subbases{$module} when doing recursive submodule cloning +# e.g. 'qt5', 'tqtc-qt5', 'qtdeclarative.git', 'https://code.qt.io/playground/qlitehtml.git' +# +# Outputs +# +# ${out_var_prefix}_${submodule_name}_url +# just the value of ${url_value} +# ${out_var_prefix}_${submodule_name}_base_git_path +# the whole url if it has a scheme, otherwise it's the value of +# ${url_value} relative to ${parent_repo_base_git_path}, so all the ../ are collapsed +# e.g. 'qtdeclarative.git' +# 'https://code.qt.io/playground/qlitehtml.git', +macro(qt_ir_parse_git_url_key out_var_prefix submodule_name url_value parent_repo_base_git_path) + qt_ir_has_url_scheme("${url_value}" has_url_scheme) + if(NOT has_url_scheme) + set(base_git_path "${parent_repo_base_git_path}/${url_value}") + + # The exact code perl code was while ($base =~ s,(?!\.\./)[^/]+/\.\./,,g) {} + # That got rid of ../ and ../../ in the path, but it broke down + # when more than two ../ were present. + # We just use ABSOLUTE to resolve the path and get rid of all ../ + # Note the empty BASE_DIR is important, otherwise the path is relative to + # ${CMAKE_CURRENT_SOURCE_DIR}. + get_filename_component(base_git_path "${base_git_path}" ABSOLUTE BASE_DIR "") + else() + set(base_git_path "${url_value}") + endif() + + set(${out_var_prefix}_${submodule_name}_url "${url_value}" PARENT_SCOPE) + set(${out_var_prefix}_${submodule_name}_base_git_path "${base_git_path}" PARENT_SCOPE) +endmacro() + +# Parses a .git/config or .gitmodules file contents and sets variables for each submodule +# starting with ${out_var_prefix}_ +# These include: +# ${out_var_prefix}_${submodule_name}_path +# the path to the submodule relative to the parent repo +# ${out_var_prefix}_${submodule_name}_branch +# the branch that should be checked out when the branch option is used +# ${out_var_prefix}_${submodule_name}_url +# the url key as encountered in the config +# ${out_var_prefix}_${submodule_name}_base_git_path +# the git base path of the submodule, either a full url or a relative path +# ${out_var_prefix}_${submodule_name}_update +# the status of the submodule, can be 'none' +# ${out_var_prefix}_${submodule_name}_status +# the status of the submodule, can be 'essential', 'addon', etc +# ${out_var_prefix}_${submodule_name}_depends +# the list of submodules that this submodule depends on +# ${out_var_prefix}_${submodule_name}_recommends +# the list of submodules that this submodule recommends to be used with +# ${out_var_prefix}_submodules +# a list of all known submodule names encountered in the file +# ${out_var_prefix}_submodules_to_remove +# a list of all submodules to remove due to update == 'none' +# ${out_var_prefix}_statuses to +# a list of all known submodule statuses like 'essential', 'addon', etc +# ${out_var_prefix}_status_${status}_submodules +# a list of all submodules with the specific status +function(qt_ir_parse_git_config_file_contents out_var_prefix) + set(options + READ_GITMODULES + READ_GIT_CONFIG + READ_GIT_CONFIG_LOCAL + ) + set(oneValueArgs + PARENT_REPO_BASE_GIT_PATH + WORKING_DIRECTORY + ) + set(multiValueArgs "") + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + qt_ir_get_working_directory_from_arg(working_directory) + + if(NOT arg_PARENT_REPO_BASE_GIT_PATH) + message(FATAL_ERROR + "qt_ir_parse_git_config_file_contents: No base PARENT_REPO_BASE_GIT_PATH specified") + endif() + set(parent_repo_base_git_path "${arg_PARENT_REPO_BASE_GIT_PATH}") + + if(arg_READ_GITMODULES) + set(read_git_config READ_GITMODULES) + elseif(arg_READ_GIT_CONFIG) + set(read_git_config READ_GIT_CONFIG) + elseif(arg_READ_GIT_CONFIG_LOCAL) + set(read_git_config READ_GIT_CONFIG_LOCAL) + else() + message(FATAL_ERROR + "qt_ir_parse_gitmodules_file_contents: No valid git config file specified") + endif() + + qt_ir_get_git_config_contents(contents + ${read_git_config} + WORKING_DIRECTORY "${working_directory}" + ) + string(REPLACE "\n" ";" lines "${contents}") + + set(known_submodules "") + set(statuses "") + set(submodules_to_remove "") + + foreach(line IN LISTS lines) + qt_ir_parse_git_key_value() + if(NOT submodule_name OR NOT key OR value STREQUAL "") + continue() + endif() + + list(APPEND known_submodules "${submodule_name}") + + if(key STREQUAL "path") + set(${out_var_prefix}_${submodule_name}_path "${value}" PARENT_SCOPE) + elseif(key STREQUAL "branch") + set(${out_var_prefix}_${submodule_name}_branch "${value}" PARENT_SCOPE) + elseif(key STREQUAL "url") + qt_ir_parse_git_url_key( + "${out_var_prefix}" "${submodule_name}" "${value}" "${parent_repo_base_git_path}") + elseif(key STREQUAL "update") + # Some repo submodules had a update = none key in their .gitmodules + # in which case we're supposed to skip initialzing those submodules, + # which the perl script did by adding -${submodule_name} to the subset. + # See qtdeclarative Change-Id: I633404f1c00d83dcbdca06a1d287623190323028 + set(${out_var_prefix}_${submodule_name}_update "${value}" PARENT_SCOPE) + if(value STREQUAL "none") + list(APPEND submodules_to_remove "-${submodule_name}") + endif() + elseif(key STREQUAL "status") + set(status_submodules "${${out_var_prefix}_status_${value}_submodules}") + list(APPEND status_submodules "${submodule_name}") + list(REMOVE_DUPLICATES status_submodules) + list(APPEND statuses "${value}") + + set(${out_var_prefix}_status_${value}_submodules "${status_submodules}") + set(${out_var_prefix}_status_${value}_submodules "${status_submodules}" PARENT_SCOPE) + set(${out_var_prefix}_${submodule_name}_status "${value}" PARENT_SCOPE) + elseif(key STREQUAL "depends") + string(REPLACE " " ";" value "${value}") + set(${out_var_prefix}_${submodule_name}_depends "${value}" PARENT_SCOPE) + elseif(key STREQUAL "recommends") + string(REPLACE " " ";" value "${value}") + set(${out_var_prefix}_${submodule_name}_recommends "${value}" PARENT_SCOPE) + endif() + endforeach() + + list(REMOVE_DUPLICATES known_submodules) + list(REMOVE_DUPLICATES submodules_to_remove) + list(REMOVE_DUPLICATES statuses) + set(${out_var_prefix}_submodules "${known_submodules}" PARENT_SCOPE) + set(${out_var_prefix}_submodules_to_remove "${submodules_to_remove}" PARENT_SCOPE) + set(${out_var_prefix}_statuses "${statuses}" PARENT_SCOPE) +endfunction() |