qt5/cmake/QtIRParsingHelpers.cmake

238 lines
9.5 KiB
CMake

# 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()