aboutsummaryrefslogtreecommitdiffstats
path: root/cmake/QtIRParsingHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtIRParsingHelpers.cmake')
-rw-r--r--cmake/QtIRParsingHelpers.cmake237
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()