#!/bin/bash # # This script pulls all the latest data from CLDR and IANA databases, builds the TZCompile project and updates the DB # Requires a UNIX-like system with FreePascal installed. # # Enjoy! # LOG_FILE=./update-compile.log function error() { echo "❌ ${1}" if [ -f "${LOG_FILE}" ]; then cat ${LOG_FILE} rm ${LOG_FILE} > /dev/null 2>&1 fi exit 1 } function warn() { echo "⚠️ ${1}" } function info() { echo "► ${1}" } function ok() { echo "✅ ${1}" } TZDB_PAS=./src/TZDBPK/TZDB.pas if [ ! -e "${TZDB_PAS}" ]; then error "Invalid current directory. Please run this script from the root of the repository." fi INPUT_VER=`cat ${TZDB_PAS} | sed -n "s/.*CComponentVersion\ *=\ *'\(.*\)';.*/\1/p"` CUSTOM_VER=0 CI=0 if [ "${1}" == "ci" ]; then CI=1 if ! command -v git &> /dev/null; then error "CI mode only supported when git is installed!" fi elif [ "${1}" != "" ]; then INPUT_VER=${1} CUSTOM_VER=1 fi IFS='.'; DOT_ARR=(${INPUT_VER}); unset IFS; VER_0=${DOT_ARR[0]} VER_1=${DOT_ARR[1]} VER_2=${DOT_ARR[2]} VER_3=${DOT_ARR[3]} if [[ ${VER_0} =~ ^[0-9]+$ ]] && [[ ${VER_1} =~ ^[0-9]+$ ]] && [[ ${VER_2} =~ ^[0-9]+$ ]] && [[ ${VER_3} =~ ^[0-9]+$ ]]; then if [ ${CUSTOM_VER} -eq 0 ]; then # Increment the build number ((VER_3++)) fi info "Will bump the version of the project to ${VER_0}.${VER_1}.${VER_2}.${VER_3}." else error "Invalid version info provided: '${INPUT_VER}'. Expected 'n.n.n.n' format." fi if [ ! -d "./tz_database_latest" ] || [ ! -e "./cldr/converter.xsl" ] || [ ! -d "./src/TZDBPK" ] || [ ! -e "./src/TZCompile/TZCompile.dpr" ]; then error "Cannot find required sub-directories. Make sure you have full repo downloaded." fi info "Pulling the latest CLDR data from GitHub..." CLDR_XML=./cldr/windowsZones.xml wget https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml -q -O ${CLDR_XML}.tmp > ${LOG_FILE} 2>&1 if [ "$?" -ne 0 ]; then warn "Failed pulling down updated CLDR Windows zone information from GitHub." rm ${CLDR_XML}.tmp else rm ${CLDR_XML} mv ${CLDR_XML}.tmp ${CLDR_XML} fi info "Converting the latest CLDR xml file to inc..." CLDR_INC=./src/TZCompile/WindowsTZ.inc cat ${CLDR_XML} | sed -n 's//GlobalCache.AddAlias("\1", "\2");/p' | sed "s/\"/'/g" > ${CLDR_INC}.tmp 2> ${LOG_FILE} || error "Failed to convert CLDR xml file to inc." rm ${CLDR_INC} > ${LOG_FILE} 2>&1 || error "Failed to remove the old CLDR inc file." mv ${CLDR_INC}.tmp ${CLDR_INC} > ${LOG_FILE} 2>&1 || error "Failed to move the new CLDR inc file." ALIASES=$(wc -l ${CLDR_INC} | sed 's/\([0-9]*\)\ .*/\1/g') || error "Failed to count the number of aliases." info "Created ${ALIASES} aliases." FPC_MAJOR=$(fpc -iV | sed 's/\([0-9]*\)\.[0-9]*\.[0-9]*/\1/g' || 0) || error "Failed to get the FreePascal major version." if [ ${FPC_MAJOR} -eq 0 ]; then error "FreePascal compiler not installed." fi if [ ${FPC_MAJOR} -lt 3 ]; then error "Expected at least FreePascal version 3." fi info "Found FreePascal version `fpc -iV` installed." rm -fr ./bin > ${LOG_FILE} 2>&1 || error "Failed to remove the bin directory." mkdir -p ./bin > ${LOG_FILE} 2>&1 || error "Failed to create the bin directory." fpc ./src/TZCompile/TZCompile.dpr -FEbin -FUbin > ${LOG_FILE} 2>&1 || error "Failed to compile the TZCompile program." info "Pulling the latest TZDB database from IANA ..." rm -rf ./iana_temp > ${LOG_FILE} 2>&1 || error "Failed to remove the iana_temp directory." wget --no-verbose --tries=3 https://data.iana.org/time-zones/tzdata-latest.tar.gz > ${LOG_FILE} 2>&1 || error "Failed to pull the latest TZDB tar ball." mkdir -p ./iana_temp > ${LOG_FILE} 2>&1 || error "Failed to create the iana_temp directory." tar -xf tzdata-latest.tar.gz -C ./iana_temp > ${LOG_FILE} 2>&1 || error "Failed to extract the latest TZDB tar ball." rm tzdata-latest.tar.gz > ${LOG_FILE} 2>&1 info "Pulling the latest ICU zones ..." wget -q https://raw.githubusercontent.com/unicode-org/icu/refs/heads/main/icu4c/source/tools/tzcode/icuzones -O ./iana_temp/icuzones > ${LOG_FILE} 2>&1 || error "Failed to pull the latest ICU zones." IANA_VERSION=$(cat ./iana_temp/version) || error "Failed to extract the IANA version." info "Current TZDB database version is v${IANA_VERSION}." FILES=( africa antarctica asia australasia backward backzone etcetera europe factory northamerica southamerica icuzones ) for fn in "${FILES[@]}"; do info "Replacing file ${fn} ..." cp ./iana_temp/${fn} ./tz_database_latest/${fn} > ${LOG_FILE} 2>&1 || error "Failed to replace file ${fn}." done rm -rf ./iana_temp > ${LOG_FILE} 2>&1 || error "Failed to remove the IANA temp directory." # CI mode checks if [ ${CI} -eq 1 ]; then CHK=$(git status -s ./cldr ./tz_database_latest) || error "Failed to check the status of the repository." if [ "${CHK}" == "" ]; then warn "Nothing has been updated. Finishing the rest of the process." exit 0 fi git add ./cldr ./tz_database_latest > ${LOG_FILE} 2>&1 || error "Failed to add the changes to the remote repository." git commit -m "chore: update to the latest tzdb/cldr" > ${LOG_FILE} 2>&1 || error "Failed to commit the changes to the remote repository." fi # Compile the IANA DB TZDB_INC=./src/TZDBPK/TZDB.inc ./bin/TZCompile ./tz_database_latest ${TZDB_INC}.temp ${IANA_VERSION} > ${LOG_FILE} 2>&1 || error "Failed to process latest TZDB data." rm ${TZDB_INC} > ${LOG_FILE} 2>&1 || error "Failed to remove the old TZDB inc file." mv ${TZDB_INC}.temp ${TZDB_INC} > ${LOG_FILE} 2>&1 || error "Failed to move the new TZDB inc file." # Run tests ./scripts/run_tests.sh || error "Tests failed!" # Update the README info "Updating README with the new version '${IANA_VERSION}'..." README=./README.md cat ${README} | sed "s/\(.*\*\*\)[0-9]*[a-z]*\(\*\*.*\)/\1${IANA_VERSION}\2/g" > ${README}.tmp 2> ${LOG_FILE} || error "Failed to update README.md file with the IANA DB version." rm ${README} > ${LOG_FILE} 2>&1 || error "Failed to remove the old README.md file." mv ${README}.tmp ${README} > ${LOG_FILE} 2>&1 || error "Failed to move the new README.md file." # Bump the version replace_tokens () { cat ${1} | sed "s/${2}/\1${3}\2/g" > ${1}.tmp 2> ${LOG_FILE} || error "Failed to bump versions in file '${1}'!" rm ${1} > ${LOG_FILE} 2>&1 || error "Failed to remove the old file '${1}'!" mv ${1}.tmp ${1} > ${LOG_FILE} 2>&1 || error "Failed to move the new file '${1}'!" } VER_MAJ="${VER_0}.${VER_1}" VER_FULL="${VER_0}.${VER_1}.${VER_2}.${VER_3}" DPROJ_FILES=$(find . -type f | grep .dproj) for DPROJ in ${DPROJ_FILES}; do info "Bumping the version of file '${DPROJ}' to '${VER_FULL}'..." cp ${DPROJ} ${DPROJ}.1 > ${LOG_FILE} 2>&1 replace_tokens ${DPROJ}.1 '\(.*FileVersion=\)[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\(.*<\/VerInfo_Keys>\)' ${VER_FULL} replace_tokens ${DPROJ}.1 '\(.*ProductVersion=\)[0-9]*\.[0-9]*\(.*<\/VerInfo_Keys>\)' ${VER_MAJ} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\(<\/VersionInfoKeys>\)' ${VER_FULL} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\.[0-9]*\(<\/VersionInfoKeys>\)' ${VER_MAJ} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VersionInfo>\)' ${VER_0} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VersionInfo>\)' ${VER_1} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VersionInfo>\)' ${VER_2} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VersionInfo>\)' ${VER_3} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VerInfo_MajorVer>\)' ${VER_0} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VerInfo_MinorVer>\)' ${VER_1} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VerInfo_Release>\)' ${VER_2} replace_tokens ${DPROJ}.1 '\(.*\)[0-9]*\(<\/VerInfo_Build>\)' ${VER_3} rm ${DPROJ} > ${LOG_FILE} 2>&1 || error "Failed to remove the old file '${DPROJ}'!" mv ${DPROJ}.1 ${DPROJ} > ${LOG_FILE} 2>&1 || error "Failed to move the new file '${DPROJ}'!" done # update the version in the .pas module as well cat ${TZDB_PAS} | sed "s/\(.*CComponentVersion\ *=\ *'\).*\(';.*\)/\1${VER_FULL}\2/g" > ${TZDB_PAS}.tmp 2> ${LOG_FILE} || error "Failed to update TZDB.pas file with the bumped version." rm ${TZDB_PAS} > ${LOG_FILE} 2>&1 || error "Failed to remove the old TZDB.pas file." mv ${TZDB_PAS}.tmp ${TZDB_PAS} > ${LOG_FILE} 2>&1 || error "Failed to move the new TZDB.pas file." # Merge the files into one DIST=./dist if [ ! -d "${DIST}" ]; then warn "Unable to locate the distribution directory. Skipping the last step." exit 0 fi info "Merging the TZDB components into one source file..." TZDB_PAS_DIST=${DIST}/TZDB.pas rm ${TZDB_PAS_DIST} > ${LOG_FILE} 2>&1 cp ./media/logo-64x64.ico ${DIST}/TZDB.ico > ${LOG_FILE} 2>&1 cat ./src/TZDBPK/TZDBPK.dpk | sed 's/TZDBPK/TZDB/g' > ${DIST}/TZDB.dpk 2> ${LOG_FILE} cleanup () { rm -fr ./xx00 > ${LOG_FILE} 2>&1 rm -fr ./xx01 > ${LOG_FILE} 2>&1 rm -fr ./xx02 > ${LOG_FILE} 2>&1 } # Split the file into pieces based in includes . csplit -s ${TZDB_PAS} '/{\$INCLUDE.*}/' {1} 2> ${LOG_FILE} if [ "$?" -ne 0 ] || [ ! -e "./xx00" ] || [ ! -e "./xx01" ] || [ ! -e "./xx02" ]; then cleanup error "Failed to split the TZDB unit file into chunks." fi # We have three chunks in here. Assemble them into final file. echo -e "{ WARNING: This file is auto-generated by update-compile.sh. Do not edit it manually! }\nunit TZDB;\n" > ${TZDB_PAS_DIST} 2> ${LOG_FILE} || error "Failed to create the TZDB.pas file." cat ./src/TZDBPK/Version.inc >> ${TZDB_PAS_DIST} 2> ${LOG_FILE} || error "Failed to append the Version.inc file to the TZDB.pas file." cat ./xx01 | sed "s/{\$INCLUDE.*}//g" >> ${TZDB_PAS_DIST} 2> ${LOG_FILE} || error "Failed to append the first chunk to the TZDB.pas file." cat ./src/TZDBPK/TZDB.inc >> ${TZDB_PAS_DIST} 2> ${LOG_FILE} || error "Failed to append the TZDB.inc file to the TZDB.pas file." cat ./xx02 | sed "s/{\$INCLUDE.*}//g" >> ${TZDB_PAS_DIST} 2> ${LOG_FILE} || error "Failed to append the second chunk to the TZDB.pas file." cleanup info "Downloading the API documentation..." wget https://raw.githubusercontent.com/wiki/pavkam/tzdb/API-Documentation.md -q -O ${DIST}/API.md > ${LOG_FILE} 2>&1 || error "Failed to download the API documentation." # Finish up in CI mode. if [ ${CI} -eq 1 ]; then TAG="${VER_0}.${VER_1}.${VER_2}.${VER_3}.${IANA_VERSION}" git add . > ${LOG_FILE} 2>&1 || error "Failed to add the changes to the remote repository." git commit -m "chore: update to IANA DB v${IANA_VERSION} and bump version" > ${LOG_FILE} 2>&1 || error "Failed to commit the changes to the remote repository." git tag "${TAG}" > ${LOG_FILE} 2>&1 || error "Failed to tag the changes to the remote repository." git push origin "${TAG}" > ${LOG_FILE} 2>&1 || error "Failed to push the changes to the remote repository." git push > ${LOG_FILE} 2>&1 || error "Failed to push the changes to the remote repository." fi ok "Finished!"