Skip to content

Instantly share code, notes, and snippets.

@3nids
Last active March 7, 2022 12:55
Show Gist options
  • Save 3nids/938119e94ec5119469f3b38d2f85788e to your computer and use it in GitHub Desktop.
Save 3nids/938119e94ec5119469f3b38d2f85788e to your computer and use it in GitHub Desktop.
QGIS settings migration script
#!/usr/bin/env bash
set -e
if [[ $1 == x ]]; then
set -x
fi
MODULE=core
MODULE_CODE=Core
MODULE_REG_CPP=src/${MODULE}/settings/qgssettingsregistry${MODULE}.cpp
MODULE_REG_H=src/${MODULE}/settings/qgssettingsregistry${MODULE}.h
DATA_FILE=scripts/settings-migration.dat
ALREADY_PROCESSED="-"
declare -a SETTING_TYPES=()
SETTING_TYPES[0]="EnumFlag::flagValue:setFlagValue"
SETTING_TYPES[1]="EnumFlag::enumValue:setEnumValue"
SETTING_TYPES[2]="Double:.to(Double|Float)\(\):"
SETTING_TYPES[3]="Integer:.toInt\(\):"
SETTING_TYPES[4]="String:.toString\(\):"
SETTING_TYPES[5]="Bool:.toBool\(\):"
SETTING_TYPES[6]="StringList:.toStringList\(\):"
declare -a HEADER_OFFSET=()
HEADER_OFFSET[0]="qgsrasterlayer.h:6"
HEADER_OFFSET[0]="pal.h:18"
declare -a ENUM_DEF2TYPE=()
# def value in code ! Enum type ! Default value if first is def override
ENUM_DEF2TYPE[0]="mSimplifyMethod.simplifyHints()!QgsVectorSimplifyMethod::SimplifyHint!SimplifyHint::NoSimplification"
ENUM_DEF2TYPE[1]="mSimplifyMethod.simplifyAlgorithm()!QgsVectorSimplifyMethod::SimplifyAlgorithm!SimplifyAlgorithm::Distance"
for _SETTING_TYPE in "${SETTING_TYPES[@]}"; do
SETTING_TYPE=$(echo ${_SETTING_TYPE} | cut -d: -f1)
SETTING_TYPE_TO=$(echo ${_SETTING_TYPE} | cut -d: -f2)
VALUE_GET=$(echo ${_SETTING_TYPE} | cut -d: -f3)
VALUE_SET=$(echo ${_SETTING_TYPE} | cut -d: -f4)
VALUE_GET=${VALUE_GET:-value}
VALUE_SET=${VALUE_SET:-setValue}
if [[ $SETTING_TYPE == EnumFlag ]]; then
INCLUDE_FILE="qgssettingsentryimpl.h"
else
INCLUDE_FILE="qgssettingsentryimpl.h"
fi
while read -u 3 -r LINE; do
# echo "****"
echo $LINE
FILE=$(echo "${LINE}" | cut -d: -f1)
CODE=$(echo "${LINE}" | cut -d: -f2,3,4,5,6)
SETTING_DEFAULT=$(echo "${CODE}" | gsed -r "s/^.*${VALUE_GET}\( QStringLiteral\( \"([^ ]+)\" \)(, ([^ ]+))?(, QgsSettings::(Section::)?(\w+))? \).*$/\1!\3!\6/")
SETTING=$(echo "${SETTING_DEFAULT}" | cut -d! -f1)
DEFAULT=$(echo "${SETTING_DEFAULT}" | cut -d! -f2)
SECTION=$(echo "${SETTING_DEFAULT}" | cut -d! -f3 | gsed 's/NoSection//')
PREFIX_STR=$(echo "$SETTING" | gsed -r 's|(/?%[0-9]+/.*)?/?[^/]+$||; s|^/||')
KEY=$(echo "$SETTING" | gsed "s|${PREFIX_STR}/||")
if [[ "${ALREADY_PROCESSED}" =~ -${SETTING}- ]]; then
echo "$SETTING already processed"
else
ALREADY_PROCESSED="-${SETTING}${ALREADY_PROCESSED}"
VALUE_DEFAULT_OVERRIDE_METHOD="setValue"
VALUE_DEFAULT_OVERRIDE_VALUE=
ENUM_SPEC=
if [[ $SETTING_TYPE == EnumFlag ]]; then
for dt in "${ENUM_DEF2TYPE[@]}"; do
if [[ $(echo "${dt}" | cut -d! -f1) == "${DEFAULT}" ]]; then
ENUM_SPEC="<$(echo "${dt}" | cut -d! -f2)>"
NEW_DEFAULT="$(echo "${dt}" | cut -d! -f3)"
if [[ -n ${NEW_DEFAULT} ]]; then
VALUE_DEFAULT_OVERRIDE_METHOD="valueWithDefaultOverride"
VALUE_DEFAULT_OVERRIDE_VALUE=${DEFAULT}
DEFAULT=${NEW_DEFAULT}
fi
break
fi
done
fi
if [[ ${DEFAULT} =~ ^m ]]; then
VALUE_DEFAULT_OVERRIDE_METHOD="valueWithDefaultOverride"
VALUE_DEFAULT_OVERRIDE_VALUE=${DEFAULT}
DEFAULT=
fi
if [[ -z ${DEFAULT} || ${DEFAULT} == "QVariant()" ]]; then
if [[ ${SETTING_TYPE} == "StringList" ]]; then
DEFAULT="QStringList()"
fi
if [[ ${SETTING_TYPE} == "String" ]]; then
DEFAULT="QString()"
fi
if [[ ${SETTING_TYPE} == "Double" ]]; then
DEFAULT="0.0"
fi
if [[ ${SETTING_TYPE} == "Integer" ]]; then
DEFAULT="0"
fi
if [[ ${SETTING_TYPE} == "Bool" ]]; then
DEFAULT="false"
fi
fi
if [[ ${SETTING_TYPE} == "Integer" ]]; then
DEFAULT=$(echo "${DEFAULT}" | gsed 's/"//g')
fi
#SETTING_VAR_NAME=$(echo "${KEY}" | gsed -r 's/^(.)/\U\1/; s/-')
SETTING_VAR_NAME=${KEY}
# PREPEND LAST PREFIX PART
if [[ ! ${PREFIX_STR} =~ ^(UI|qgis)$ ]]; then
SETTING_VAR_NAME=$(echo "${PREFIX_STR}" | gsed 's|^/?(\w+/)+||g'| gsed -r 's/^(.)/\U\1/')$(echo "${SETTING_VAR_NAME}" | gsed -r 's/^(.)/\U\1/')
fi
SETTING_VAR_NAME="settings"$(echo "${SETTING_VAR_NAME}" | gsed -r 's/^(.*)$/_\1/; s|/|_|g; s/__/_/; s/(_|-)([a-z])/\1\U\2/g; s/(-|_)//g; s/^_//;')
PREFIX_STR=$(echo "${SECTION,,}_${PREFIX_STR}" | gsed 's/^_//')
PREFIX_VAR=$(echo "${PREFIX_STR}" | gsed -r 's/^(.*)$/_\1/; s|/|_|g; s/__/_/; s/(_|-)([a-z])/\1\U\2/g; s/-//g; s/^_//;' | gsed -r 's/^(.*)$/\U\1/')
if [[ -z $PREFIX_STR ]]; then
PREFIX_VAR_COMPLETE="QgsSettings::Prefix::NO_PREFIX"
PREFIX_VAR="NO_PREFIX"
else
PREFIX_VAR_COMPLETE="QgsSettings::Prefix::${PREFIX_VAR}"
fi
# for regexes
SETTING_ESC=$(echo "${SETTING}" | gsed 's|/|.|g')
HEADER=$(echo ${FILE} | gsed 's|\.cpp$|.h|')
BASECLASS=$(basename ${HEADER} | gsed 's/\.h$//')
if [[ ! ${HEADER} =~ \.h$ ]]; then
echo "wrong header: $HEADER"
exit 1
fi
# echo "$HEADER $BASECLASS"
HAS_CLASS=$(git grep -i -c "^ *class ${MODULE}_EXPORT ${BASECLASS}\b" ${HEADER} | cut -d: -f2)
CLASS_DEF=$(git grep -i "^ *class ${MODULE}_EXPORT ${BASECLASS}\b" ${HEADER} | cut -d: -f2 | gsed -r "s/^ *class ${MODULE}_EXPORT (${BASECLASS}).*$/\1/i")
echo -e " FILE: $FILE\n SETTING_TYPE: $SETTING_TYPE\n SETTING: $SETTING\n DEFAULT: $DEFAULT\n KEY: $KEY\n PREFIX_STR: $PREFIX_STR\n PREFIX_VAR: $PREFIX_VAR\n SECTION: $SECTION\n CLASS_DEF: $CLASS_DEF\n HAS_CLASS: $HAS_CLASS\n SETTING_VAR_NAME: $SETTING_VAR_NAME\n HEADER: $HEADER\n ENUM_SPEC: $ENUM_SPEC"
MODE=0
while read C_LINE; do
C_SETTING=$(echo "$C_LINE" | cut -d: -f1)
C_ACTION=$(echo "$C_LINE" | cut -d: -f2)
if [[ "$C_SETTING" == "$SETTING" ]]; then
echo "setting already in config: ${C_ACTION}"
MODE="${C_ACTION}"
break
fi
done<${DATA_FILE}
if [[ "${MODE}" == 0 ]]; then
echo "***"
echo -e "Add ${SETTING}" to
echo -e " c) class ${CLASS_DEF}"
echo -e " r) registry"
echo -e " s) skip and continue"
echo -e " e) exit"
while read -n 1 n; do
echo ""0
case $n in
c)
echo -e "add to class"
MODE=class
# https://unix.stackexchange.com/a/533708/238014
#gsed -e "/^ *class ${MODULE}_EXPORT ${BASECLASS}/{:a; N; /\n *public:/!ba; a\XXXX" -e '}' ${HEADER}
break
;;
r)
echo -e "add to registry"
MODE=registry
break
;;
s)
echo -e "skip"
MODE=SKIP
break
;;
e)
exit 1
;;
*) invalid option;;
esac
done
fi
if [[ ${MODE} != skip ]]; then
echo "$SETTING:$MODE" >> ${DATA_FILE}
sort -u -o ${DATA_FILE} ${DATA_FILE}
if [[ ${MODE} == class ]]; then
SETTING_VAR_NAME_COMPLETE="${CLASS_DEF}::${SETTING_VAR_NAME}"
SETTING_VAR_NAME_CLASS="${SETTING_VAR_NAME}"
SETTING_VAR_NAME_REGISTRY="${CLASS_DEF}::${SETTING_VAR_NAME}"
CLASS_FOUND=0
shopt -s nocasematch
IFS=''
INCLUDE_IMPL_COUNT=$(git grep -c "#include \"${INCLUDE_FILE}\"" ${HEADER} | cut -d: -f2)
if [[ -z $INCLUDE_IMPL_COUNT ]]; then
OFFSET=20
for hf in "${HEADER_OFFSET[@]}"; do
if [[ $(echo "${hf}" | cut -d: -f1) == $(basename ${HEADER}) ]]; then
OFFSET=$((${OFFSET}+$(echo "${hf}" | cut -d: -f2)))
break
fi
done
gsed -i "${OFFSET} i #include \"${INCLUDE_FILE}\"" ${HEADER}
fi
LINE_NUMBER=0
while read F_LINE; do
((LINE_NUMBER+=1))
if [[ ${CLASS_FOUND} == 0 ]]; then
#echo '[[ "'${F_LINE//\"/\\\"g}'" =~ class\ '${MODULE}'_EXPORT\ '${BASECLASS}' ]]'
if eval '[[ "'${F_LINE//\"/\\\"g}'" =~ class\ '${MODULE}'_EXPORT\ '${BASECLASS}' ]]'; then
echo "CLASS_FOUND"
CLASS_FOUND=1
fi
else
if [[ "${F_LINE}" =~ ^\ *public:$ ]]; then
echo "public found"
gsed -i "$(( $LINE_NUMBER+2 )) i \ \ \ \ static const inline QgsSettingsEntry${SETTING_TYPE}${ENUM_SPEC} ${SETTING_VAR_NAME} = QgsSettingsEntry${SETTING_TYPE}${ENUM_SPEC}( QStringLiteral( \"${KEY}\" ), ${PREFIX_VAR_COMPLETE}, ${DEFAULT} ) SIP_SKIP;" ${HEADER}
break
fi
fi
done < ${HEADER}
shopt -u nocasematch
else
SETTING_VAR_NAME_COMPLETE="QgsSettingsRegistry${MODULE_CODE}::${SETTING_VAR_NAME}"
SETTING_VAR_NAME_CLASS="QgsSettingsRegistry${MODULE_CODE}::${SETTING_VAR_NAME}"
SETTING_VAR_NAME_REGISTRY="${SETTING_VAR_NAME}"
gsed -i "185 i \ \ \ \ static const inline QgsSettingsEntry${SETTING_TYPE}${ENUM_SPEC} ${SETTING_VAR_NAME_REGISTRY} = QgsSettingsEntry${SETTING_TYPE}${ENUM_SPEC}( QStringLiteral( \"${KEY}\" ), ${PREFIX_VAR_COMPLETE}, ${DEFAULT} );" ${MODULE_REG_H}
fi
PREFIX_DECL_COUNT=$(git grep -c "static const inline char \*${PREFIX_VAR} = \"${PREFIX_STR}\";" src/core/settings/qgssettings.h | cut -d: -f2)
if [[ -z $PREFIX_DECL_COUNT ]]; then
gsed -i "105 i \ \ \ \ \ \ \ \ static const inline char *${PREFIX_VAR} = \"${PREFIX_STR}\";" src/core/settings/qgssettings.h
fi
# Add setting to registry
HEADER_BASENAME=$(basename ${HEADER})
gsed -i "102 i \ \ addSettingsEntry( \&${SETTING_VAR_NAME_REGISTRY} );" ${MODULE_REG_CPP}
INCLUDE_REG_COUNT=$(git grep -c "#include \"${HEADER_BASENAME}\"" ${MODULE_REG_CPP} | cut -d: -f2)
if [[ -z $INCLUDE_REG_COUNT ]]; then
gsed -i "28 i #include \"${HEADER_BASENAME}\"" ${MODULE_REG_CPP}
fi
while read -r S_FILE; do
if [[ -z ${S_FILE} ]]; then continue; fi
SVARNAME=$([[ ${S_FILE} =~ ${HEADER} ]] && echo "${SETTING_VAR_NAME}" || echo "${SETTING_VAR_NAME_COMPLETE}")
echo "fixing ${S_FILE} with $SVARNAME"
gsed -i -r "s/(QgsSettings|QSettings|mSettings|settings)\.${VALUE_GET}\( QStringLiteral. .${SETTING_ESC}. \)(, ([^ ]+))* \)${SETTING_TYPE_TO}/${SVARNAME}.${VALUE_DEFAULT_OVERRIDE_METHOD}(${VALUE_DEFAULT_OVERRIDE_VALUE})/" ${S_FILE}
gsed -i -r "s/(QgsSettings|QSettings|mSettings|settings)\.${VALUE_SET}\( QStringLiteral. .${SETTING_ESC}. \), ([^ ]+)(, ([^ ]+))? \)/${SVARNAME}.setValue( \2 )/" ${S_FILE}
if [[ ${MODE} == registry ]]; then
INCLUDE_IMPL_COUNT=$(git grep -c "#include \"qgssettingsregistry${MODULE}.h\"" ${S_FILE} | cut -d: -f2)
if [[ -z $INCLUDE_IMPL_COUNT ]]; then
gsed -i "20 i #include \"qgssettingsregistry${MODULE}.h\"" ${S_FILE}
fi
fi
done<<<$(git grep -E -c "${SETTING_ESC}" src | cut -d: -f1)
fi
git add src python tests
git commit -m "migrate setting ${SETTING} to ${MODE} (${SETTING_TYPE}, ${MODULE})"
fi
done 3< <( unbuffer ag --noaffinity --only-matching --nonumber --no-group --nocolor -G '\.cpp$' "settings\.${VALUE_GET}\( QStringLiteral\( \"[^ ]+\" \)(, [^ ]+)+ \)${SETTING_TYPE_TO}" src/${MODULE})
done
# cleanup
while read -r FILE; do
if [[ $FILE =~ ^src/core/settings ]]; then
echo "skip $FILE"
continue
fi
echo "clean $FILE"
gsed -i -r '/^ *(const *)?QgsSettings (s|mS)ettings;/d' $FILE
done<<<$(ag -c QgsSettings src/${MODULE} | cut -d: -f1)
git add src
git commit -m "clean up QgsSettings in src/${MODULE}"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment