#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## general example usage:

## 1)
#make_cross_build_platform_list="amd64 arm64" ./build-steps.d/*_cowbuilder-setup --allow-untagged true --allow-uncommitted true --flavor source --target root

## 2)
#make_cross_build_platform_list="amd64 arm64" ./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor source --target root

## specific example usage:

#make_cross_build_platform_list="amd64 arm64" ./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor source --target root --function download_tpo_packages

#./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor source --target virtualbox --function download_packages_from_debian_sid

## TODO: always check packages/kicksecure/anon-shared-build-apt-sources-tpo/etc/apt/sources.list.d/torproject.sources first!

set -x
set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose
export LC_ALL=C

true "INFO: Currently running script: ${BASH_SOURCE[0]} $*"

MYDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

cd "${MYDIR}"
cd ..
cd help-steps

dist_build_source_run="true"

source pre
source variables
source "${dist_developer_meta_files_folder}/usr/libexec/developer-meta-files/package-build-freshness.bsh"

cd "${MYDIR}"
cd ..

download_tpo_packages() {
   ## https://forums.whonix.org/t/tor-integration-in-whonix/10593/2
   available_architectures_for_download_only_list="amd64 arm64"
   dist_build_special_packages_chroot_script="${dist_source_help_steps_folder}/repo_download_chroot_script"
   repo_signing_key="${source_code_folder_dist}/packages/kicksecure/anon-shared-build-apt-sources-tpo/usr/share/anon-shared-build-apt-sources-tpo/tpoarchive-keys.d/torprojectarchive.asc"
   repo_sources_list="${dist_build_sources_list_torproject}"
   temp_newer_packages="${binary_build_folder_dist}/temp_packages_tpo"
   export apt_target_release="none"
   export download_source_package="true"

   ## Selected packages we want to mirror from deb.torproject.org to Kicksecure local/remote repository.
   newer_package_list="tor tor-geoipdb deb.torproject.org-keyring"

   build_run_function get_newer_packages_from_third_party_repositories "$@"
}

## Everything after the unconditional error/return below is intentionally
## retained (an incomplete future implementation, documented via the XXX/TODO
## notes) and is therefore unreachable; suppress the SC2317 warnings for it.
# shellcheck disable=SC2317
download_virtualbox_packages_virtualbox_org() {
   ## Only when using '--target virtualbox'.
   if [ ! "${dist_build_virtualbox:-}" = "true" ]; then
      true "${green}INFO: Skipping ${under}${FUNCNAME[0]}${eunder}, because dist_build_virtualbox is not set to 'true'.${reset}"
      return 0
   fi

   error "Function download_virtualbox_packages_virtualbox_org is currently not needed and incomplete."
   return 0

   ## XXX: Better set to "amd64" which is the only actually available platform for this repo_sources_list?
   ## Or better set available_architectures_for_download_only_list="all" to make the build process fail early?
   ## But does not matter since this code is only processed when using '--target virtualbox'.
   ## Only platform amd64 is available from this repo_sources_list.
   ## (Even packages.debian.org having amd64 only https://packages.debian.org/sid/virtualbox at time of writing.)
   ## Creating '--target virtualbox' on platforms other than amd64 such as arm64 would fail anyhow since
   ## nobody is providing packages for these platforms. At time of writing, nobody reported trying that either.
   available_architectures_for_download_only_list="amd64"
   dist_build_special_packages_chroot_script="${dist_source_help_steps_folder}/repo_download_chroot_script"
   repo_signing_key="${source_code_folder_dist}/build_sources/oracle_vbox_2016.asc"
   ## TODO: repo_sources_list - low priority - currently not needed
   repo_sources_list=""
   temp_newer_packages="${binary_build_folder_dist}/temp_packages_virtualbox_org"
   export apt_target_release="none"
   ## Not available from that repository but from Debian directly.
   export download_source_package="false"

   ## Selected packages we want to mirror from repo_sources_list to Whonix local/remote repository.
   newer_package_list="virtualbox-7.2"

   build_run_function get_newer_packages_from_third_party_repositories "$@"
}

download_packages_from_debian_sid() {
   ## Using 'sid' instead of 'unstable'.
   ## github.com/grml/grml-debootstrap/issues/37

   available_architectures_for_download_only_list="all"
   dist_build_special_packages_chroot_script="${dist_source_help_steps_folder}/repo_download_chroot_script"
   repo_signing_key="none"
   repo_sources_list="${dist_build_sources_list_debian_sid}"
   temp_newer_packages="${binary_build_folder_dist}/temp_packages_debian_sid"
   export apt_target_release="sid"
   export download_source_package="true"

   newer_package_list=""

   #if [ ! "${dist_build_iso:-}" = "true" ]; then
      #return 0
   #fi
   ## On Debian trixie, installing calamares from sid has lead to (dependency or other?) issues
   ## A proper backport would be required.
   #if [ "${dist_build_iso:-}" = "true" ]; then
      #newer_package_list="calamares"
   #fi

   #if [ "${dist_build_virtualbox:-}" = "true" ] || [ "${dist_build_iso:-}" = "true" ]; then
      ## See:
      ## https://forums.whonix.org/t/challenges-installing-virtualbox/9984/7
      ##
      ## comment outdated:
      ##
      ## Upstream virtualbox.org does not provide guest additions debian packages:
      ## (Neither VirtualBox guest addition ISO nor VirtualBox guest addition debian package.)
      ## https://www.virtualbox.org/ticket/734
      ##
      ## https://packages.debian.org/bullseye/virtualbox-guest-additions-iso
      ## provides:
      ## /usr/share/virtualbox/VBoxGuestAdditions.iso
      ##
      ## nonfreedom vs freedom discussion:
      ## https://www.whonix.org/wiki/Dev/VirtualBox#VirtualBox_Unavailable_in_Debian_main_due_to_Licensing_Issues
      ## https://forums.virtualbox.org/viewtopic.php?f=10&t=21374&p=477656#p477656
      ## https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=962311
      ## https://www.virtualbox.org/ticket/19751

      ## Selected packages we want to mirror from repo_sources_list to derivative local/remote repository.

      ## quote debian/changelog
      ## * Drop virtualbox guest modules dkms and sources, as in Ubuntu and Debian,
      ## all kernel flavours provide those from the upstream kernel since at least
      ## focal 20.04 LTS.
      ## Thus these are not needed anymore. LP: #1933248
      ##
      ## please drop virtualbox-guest-dkms virtualbox-guest-source
      ## https://bugs.launchpad.net/ubuntu/+source/virtualbox/+bug/1933248

      ## We package Guest Additions ourselves now.
      #newer_package_list="virtualbox-guest-additions-iso"

      #if [ "${dist_build_target_arch:-}" = "amd64" ]; then
         ## These packages are no longer needed for installed systems. We
         ## install Guest Additions from virtualbox-guest-additions-iso now.
         #newer_package_list+=" virtualbox-guest-utils "
         #newer_package_list+=" virtualbox-guest-x11 "

         #if [ "${dist_build_virtualbox:-}" = "true" ]; then
            ## These packages are not needed for the ISO.
            ##
            ## These 'unstable' ('sid') packages are longer compatible with 'trixie'.
            #newer_package_list+=" virtualbox "
            #newer_package_list+=" virtualbox-dkms "
            #newer_package_list+=" virtualbox-qt "
            #newer_package_list+=" virtualbox-source "
         #fi
      #fi
   #fi

   if [ -n "${newer_package_list}" ]; then
      build_run_function get_newer_packages_from_third_party_repositories "$@"
   fi
}

## usage example for manual testing:
#./build-steps.d/*_create-debian-packages --allow-untagged true --allow-uncommitted true --flavor source --target iso --function download_packages_from_debian_forky_backports
## No backports packages are needed for non-ISO builds at present. Keeping
## downloader code for future reference.
# shellcheck disable=SC2317
download_packages_from_debian_forky_backports() {
   available_architectures_for_download_only_list="all"
   dist_build_special_packages_chroot_script="${dist_source_help_steps_folder}/repo_download_chroot_script"
   repo_signing_key="none"
   repo_sources_list="${dist_build_sources_list_debian_forky_backports}"
   temp_newer_packages="${binary_build_folder_dist}/temp_packages_debian_forky_backports"
   export apt_target_release="forky-backports"
   export download_source_package="false"

   newer_package_list=""

   ## No need for calamares from debian.backports in Trixie at the moment.
   #if [ "${dist_build_iso:-}" = "true" ]; then
   #   true "INFO: Download package from backports for ISO build..."
   #   newer_package_list="calamares"
   #else
   #   true "INFO: No packages from backports required for non-ISO builds at this time, skipping, ok."
   #   return 0
   #fi

   true "INFO: No packages from backports required for non-ISO builds at this time, skipping, ok."
   return 0

   build_run_function get_newer_packages_from_third_party_repositories "$@"
}

get_newer_packages_from_third_party_repositories() {
   true "${cyan}${BASH_SOURCE[0]} INFO: Downloading newer packages from third-party repository... ${reset}"

   local cow_folder base_folder

   if [ "${temp_newer_packages:-}" = "" ]; then
      error "variable temp_newer_packages is unset!"
      exit 1
   fi

   ${SUDO_TO_ROOT} safe-rm --one-file-system --recursive --force -- "${temp_newer_packages}"
   mkdir --parents -- "${temp_newer_packages}"

   for dist_build_multiarch_package_item in ${make_cross_build_platform_list} ; do
      export dist_build_multiarch_package_item

      architecture_available=""
      for available_architectures_for_download_only_item in ${available_architectures_for_download_only_list} ; do
         if [ "all" = "${available_architectures_for_download_only_item}" ]; then
            true "${cyan}INFO: available_architectures_for_download_only_item: '${available_architectures_for_download_only_item}'${reset}"
            architecture_available="true"
            continue
         fi
         if [ "${dist_build_multiarch_package_item:-}" = "${available_architectures_for_download_only_item}" ]; then
            true "${cyan}INFO: available_architectures_for_download_only_item: '${available_architectures_for_download_only_item}'${reset}"
            architecture_available="true"
            continue
         fi
      done
      if [ ! "${architecture_available:-}" = "true" ]; then
         true "${red}${bold}INFO: Skipping,
download newer_package_list: ${under}${newer_package_list}${eunder}
for platform: ${under}${dist_build_multiarch_package_item}${eunder}
because dist_build_multiarch_package_item: ${dist_build_multiarch_package_item}
is not included in available_architectures_for_download_only_list: ${available_architectures_for_download_only_list}${reset}"
         return 0
      fi

      if [ "${make_use_cowbuilder:-}" = "true" ]; then
         ## Implemented in help-steps/variables.
         ## sets:
         ## cow_folder
         ## base_folder
         set_cowbuilder_folders

         ${SUDO_TO_ROOT} test -d "${base_folder}"
         ${SUDO_TO_ROOT} "${dist_source_help_steps_folder}/mount-cleanup" -- "${base_folder}"
         if_no_pbuilder_base_folder=""
      else
         base_folder="${binary_build_folder_dist}/no-pbuilder/base"
         ## Not needed. For completeness sake. Just in case.
         cow_folder="${binary_build_folder_dist}/no-pbuilder/cow"
         if_no_pbuilder_base_folder="${base_folder}"
      fi
      export if_no_pbuilder_base_folder

      ${SUDO_TO_ROOT} mkdir --parents -- "${base_folder}/var/cache/derivative-maker/temporary-repository"

      if [ "${repo_signing_key:-}" = "none" ]; then
         ${SUDO_TO_ROOT} safe-rm -f -- "${base_folder}/var/cache/derivative-maker/temporary-repository/repo_signing_key.asc"
      else
         ${SUDO_TO_ROOT} cp -- "${repo_signing_key}" "${base_folder}/var/cache/derivative-maker/temporary-repository/repo_signing_key.asc"
      fi

      if [ "${repo_sources_list:-}" = "none" ]; then
         ${SUDO_TO_ROOT} safe-rm -f -- "${base_folder}/var/cache/derivative-maker/temporary-repository/newer.sources"
      else
         ${SUDO_TO_ROOT} cp -- "${repo_sources_list}" "${base_folder}/var/cache/derivative-maker/temporary-repository/newer.sources"
      fi

      local benchmark_time_start benchmark_took_time
      benchmark_time_start="${SECONDS}"
      local dist_build_download_script_exit_code
      dist_build_download_script_exit_code=0

      if [ "${make_use_cowbuilder:-}" = "true" ]; then
         ## $COWBUILDER_PREFIX is intentionally unquoted: it is set to
         ## something like 'env http_proxy=... foo=bar' that must split
         ## into multiple argv slots.
         # shellcheck disable=SC2086
         ${SUDO_TO_ROOT} \
            --preserve-env \
               ${COWBUILDER_PREFIX:-} \
               newer_package_list="${newer_package_list}" \
               temp_newer_packages="${temp_newer_packages}" \
                  ${UNSHARE_PID_NS:-} \
                  cowbuilder \
                     --host-arch "${host_architecture}" \
                     --architecture "${dist_build_target_arch}" \
                     --configfile "${dist_build_pbuilder_config_file}" \
                     --execute "${dist_build_special_packages_chroot_script}" \
                     --basepath "${base_folder}" \
                     --buildplace "${cow_folder}" \
                     --bindmounts "${temp_newer_packages}" \
                     || { dist_build_download_script_exit_code="$?" ; true; };

         if [ -d "${base_folder}" ]; then
            ${SUDO_TO_ROOT} "${dist_source_help_steps_folder}/mount-cleanup" -- "${base_folder}"
         fi
      else
         export newer_package_list
         export temp_newer_packages
         ${SUDO_TO_ROOT} "${dist_build_special_packages_chroot_script}" || { dist_build_download_script_exit_code="$?" ; true; };

         ## XXX
         ## help-steps/repo_download_chroot_script messes up APT downloaded sources
         ## $SUDO_TO_ROOT is intentionally word-split (may be 'sudo --...').
         # shellcheck disable=SC2086
         retry_run --tries 4 --delay 15 -- ${SUDO_TO_ROOT} apt-get "${DIST_APTGETOPT[@]}" update
      fi

      ${SUDO_TO_ROOT} chown --recursive "${user_name}:${user_name}" "${temp_newer_packages}"

      benchmark_took_time="$(benchmark_since "${benchmark_time_start}")"
      true "${cyan}INFO: Done with function ${under}${FUNCNAME[0]}${eunder} (benchmark: ${benchmark_took_time}) ${reset}"

      if [ ! "${dist_build_download_script_exit_code:-}" = "0" ]; then
         return "${dist_build_download_script_exit_code}"
      fi
   done

   true "${cyan}${BASH_SOURCE[0]} INFO: Completed downloading newer packages from third-party repository. ${reset}"

   reprepro_add_newer_packages_from_third_party_repositories
}

reprepro_add_newer_packages_from_third_party_repositories() {
   true "${cyan}${BASH_SOURCE[0]} INFO: Adding newer packages from third-party repository to reprepro... ${reset}"
   if [ "${temp_newer_packages:-}" = "" ]; then
      error "Variable temp_newer_packages is empty!"
   fi

   ## read by dm-reprepro-wrapper
   derivative_repository_name="kicksecure"
   export derivative_repository_name

   local file_name package_absolute_path file_name_only

   for file_name in "${temp_newer_packages}/"*".deb" ; do
      package_absolute_path="$(readlink -f "${file_name}")"
      file_name_only="${package_absolute_path##*/}"
      package=${file_name_only%%_*}

      "${dist_developer_meta_files_folder}/usr/bin/dm-reprepro-wrapper" remove "${dist_build_apt_codename}" "${package}" || true
      "${dist_developer_meta_files_folder}/usr/bin/dm-reprepro-wrapper" removesrc "${dist_build_apt_codename}" "${package}" || true
   done

   for file_name in "${temp_newer_packages}/"*".dsc" ; do
      package_absolute_path="$(readlink -f "${file_name}")"
      file_name_only="${package_absolute_path##*/}"
      package=${file_name_only%%_*}

      "${dist_developer_meta_files_folder}/usr/bin/dm-reprepro-wrapper" remove "${dist_build_apt_codename}" "${package}" || true
      "${dist_developer_meta_files_folder}/usr/bin/dm-reprepro-wrapper" removesrc "${dist_build_apt_codename}" "${package}" || true
   done

   for file_name in "${temp_newer_packages}/"*".deb" ; do
      [ -e "${file_name}" ] || continue
      package_absolute_path="$(readlink -f "${file_name}")"
      "${dist_developer_meta_files_folder}/usr/bin/dm-reprepro-wrapper" includedeb "${dist_build_apt_codename}" "${package_absolute_path}"
   done

   for file_name in "${temp_newer_packages}/"*".dsc" ; do
      [ -e "${file_name}" ] || continue
      package_absolute_path="$(readlink -f "${file_name}")"
      "${dist_developer_meta_files_folder}/usr/bin/dm-reprepro-wrapper" includedsc "${dist_build_apt_codename}" "${package_absolute_path}"
   done

   true "${cyan}${BASH_SOURCE[0]} INFO: Added newer packages from third-party repository to reprepro. ${reset}"
}

create_derivative_distribution_debian_packages() {
   true "${cyan}${BASH_SOURCE[0]} INFO: Creating Derivative Debian Packages... ${reset}"

   ## Sanity test.
   printf '%s\n' "" | wc -l >/dev/null

   local number_folders_total
   number_folders_total="$(find "${source_code_folder_dist}/packages" -mindepth 2 -maxdepth 2 -type d -printf '\n' | wc -l)" || true

   shopt -s nullglob
   shopt -s dotglob

   local genmkfile_bin="${source_code_folder_dist}/packages/kicksecure/genmkfile/usr/bin/genmkfile"

   ## Phase 1: collect the packages that actually build on this target architecture. Reading
   ## debian/control directly (no 'pushd') so the list can then be driven in parallel.
   local derivative_name_item item base_name control_file
   local -a package_build_list=()
   for derivative_name_item in ${derivative_name_list} ; do
      for item in ${dist_build_only_packages} ; do
         item="${source_code_folder_dist}/packages/${derivative_name_item}/${item}"
         if [ ! -d "${item}" ]; then
            true "INFO: Not a folder: ${item}"
            continue
         fi
         control_file="${item}/debian/control"
         if [ ! -f "${control_file}" ]; then
            true "INFO: no debian/control, skipping: ${item}"
            continue
         fi
         base_name="${item##*/}"
         ## 'Architecture:' currently in ./packages/$derivative_name_item/ folder: all / any /
         ## amd64 / linux-amd64.
         local architecture_field
         architecture_field="$(grep --invert-match -- "#" "${control_file}" | grep -- "^Architecture:" | cut -d' ' -f2-)" || true
         case "${architecture_field}" in
            *all*)
               true "DEBUG: ${base_name} Architecture: all"
               ;;
            *any*)
               true "DEBUG: ${base_name} Architecture: any"
               ;;
            *amd64*)
               ## Covers both 'amd64' and 'linux-amd64' (the latter contains 'amd64').
               if [ ! "${dist_build_target_arch:-}" = "amd64" ]; then
                  true "${cyan}INFO: Skipping platform 'amd64' specific package ${base_name} as expected.${reset}"
                  continue
               fi
               ;;
            *)
               grep -- "Architecture:" "${control_file}" || true
               error "unknown Architecture: in ${control_file}"
               ;;
         esac
         ## Fast local iteration (opt-in): skip a package already published in
         ## the local reprepro repo and not modified after that.
         if [ "${dist_build_skip_published_packages:-}" = "true" ] \
            && ! dm_package_needs_rebuild "${item}" "${dist_build_apt_codename}" "${derivative_name_item}"; then
            true "${cyan}INFO: Skipping up-to-date package ${base_name} (already published and not modified).${reset}"
            continue
         fi
         package_build_list+=("${item}")
      done
   done

   local total_to_build="${#package_build_list[@]}"
   true "${cyan}INFO: ${total_to_build} derivative package(s) to build (of ${number_folders_total} folders).${reset}"
   if [ "${total_to_build}" -lt 1 ]; then
      true "${cyan}${BASH_SOURCE[0]} INFO: No derivative debian packages need built.${reset}"
      return 0
   fi

   ## Build ${dist_build_package_jobs} packages in parallel. No package
   ## build-depends on another one, so no dependency ordering is needed.
   local max_jobs=1
   if [ -n "${dist_build_package_jobs:-}" ]; then
      case "${dist_build_package_jobs}" in
         '' | *[!0-9]* | 0* )
            error "dist_build_package_jobs must be a positive integer (got: '${dist_build_package_jobs}')."
            ;;
      esac
      if [ ! "${make_use_cowbuilder:-}" = "true" ]; then
         true "${cyan}INFO: --package-jobs=${dist_build_package_jobs} requested, but cowbuilder is not in use; building serially.${reset}"
      else
         max_jobs="${dist_build_package_jobs}"
         if [ "${max_jobs}" -gt "${total_to_build}" ]; then
            true "${cyan}INFO: --package-jobs=${max_jobs} exceeds the ${total_to_build} package(s) to build; using ${total_to_build}.${reset}"
            max_jobs="${total_to_build}"
         fi
      fi
   fi
   true "${cyan}INFO: building packages with up to ${max_jobs} parallel job(s).${reset}"

   ## Phase 2: build (deb-cleanup + deb-pkg) via a free-slot job pool. A worker records its package
   ## name to the fail flag on failure; the pool never lets two workers share a cow slot.
   local pkg_fail_list_file
   pkg_fail_list_file="$(mktemp)"
   safe-rm --force -- "${pkg_fail_list_file}"

   build_one_derivative_package() {
      local pkgdir="$1" slot="$2" name
      name="${pkgdir##*/}"
      if ! (
         cd -- "${pkgdir}" || exit 1
         export make_cow_suffix=".${slot}"
         "${genmkfile_bin}" deb-cleanup
         if [ "${make_use_cowbuilder:-}" = "false" ]; then
            "${genmkfile_bin}" deb-build-dep
         fi
         "${genmkfile_bin}" deb-pkg
      ); then
         printf '%s\n' "${name}" >> "${pkg_fail_list_file}"
      fi
   }

   local -a slot_pid
   local i slot launched=0
   for (( i=0; i<max_jobs; i++ )); do
      slot_pid[i]=0
   done
   ## TODO: Add an option for failing the build immediately after the first
   ## package build failure?
   for item in "${package_build_list[@]}"; do
      slot=-1
      while [ "${slot}" -lt 0 ]; do
         for (( i=0; i<max_jobs; i++ )); do
            if [ "${slot_pid[i]}" -eq 0 ]; then
               slot="${i}"
               break
            fi
            if ! kill -0 "${slot_pid[i]}" 2>/dev/null ; then
               wait "${slot_pid[i]}" 2>/dev/null || true
               slot="${i}"
               break
            fi
         done
         if [ "${slot}" -lt 0 ]; then
            wait -n 2>/dev/null || true
         fi
      done
      launched="$(( launched + 1 ))"
      true "${cyan}INFO: [slot ${slot}] building (${launched}/${total_to_build}): ${item##*/} ${reset}"
      build_one_derivative_package "${item}" "${slot}" &
      slot_pid[slot]="$!"
   done
   for (( i=0; i<max_jobs; i++ )); do
      if [ "${slot_pid[i]}" -ne 0 ]; then
         wait "${slot_pid[i]}" 2>/dev/null || true
      fi
   done
   if [ -f "${pkg_fail_list_file}" ]; then
      local failed_packages
      failed_packages="$(tr '\n' ' ' < "${pkg_fail_list_file}")"
      safe-rm --force -- "${pkg_fail_list_file}"
      error "Derivative package build(s) failed: ${failed_packages}"
   fi
   safe-rm --force -- "${pkg_fail_list_file}"

   ## Phase 3: load packages into reprepro serially.
   local reprepro_current=0
   local manifest_path
   manifest_path="$(dm_source_hash_manifest_path "${dist_build_apt_codename}")"
   for item in "${package_build_list[@]}"; do
      reprepro_current="$(( reprepro_current + 1 ))"
      pushd "${item}" >/dev/null
      true "${cyan}INFO: reprepro (${reprepro_current}/${total_to_build}): ${item##*/}${reset}"
      "${genmkfile_bin}" reprepro-remove
      "${genmkfile_bin}" reprepro-add
      ## Record the source content hash so the next --skip-published-packages build
      ## skips this package unless its source changes again.
      dm_source_hash_record \
         "${manifest_path}" \
         "$(dpkg-parsechangelog --show-field Source --file "${item}/debian/changelog")" \
         "$(dm_package_source_hash "${item}")"
      popd >/dev/null
   done

   true "${cyan}${BASH_SOURCE[0]} INFO: Created Derivative Debian Packages. ${reset}"
}

create-debian-packages() {
   if [ "${dist_build_apt_codename:-}" = "" ]; then
      error "Variable dist_build_apt_codename is empty!"
   fi
   true "${cyan}${BASH_SOURCE[0]} INFO: Going to update ${under}${dist_build_apt_codename}${eunder} APT repository... ${reset}"
   true "make_cross_build_platform_list: ${under}${make_cross_build_platform_list}${eunder}"
   #sleep 3

   if [ ! "${FUNCTION:-}" = "" ]; then
      ${FUNCTION}
      return 0
   fi

   #build_run_function download_virtualbox_packages_virtualbox_org "$@"
   build_run_function download_packages_from_debian_sid "$@"
   build_run_function download_packages_from_debian_forky_backports "$@"

   ## https://www.whonix.org/wiki/Dev/Tor
   ## https://forums.whonix.org/t/tor-integration-in-whonix/10593/57
   build_run_function download_tpo_packages "$@"

   build_run_function create_derivative_distribution_debian_packages "$@"
}

main() {
   if [ "${build_dry_run:-}" = "true" ]; then
      true "${bold}${cyan}INFO: dry-run, skipping ${BASH_SOURCE[0]}. ${reset}"
      return 0
   fi

   ## '--skip-packages': reuse the whole local package repo from the prior build (debug speed-up).
   if [ "${dist_build_skip_packages:-}" = "true" ]; then
      true "${bold}${cyan}INFO: --skip-packages, reusing the existing local package repo, skipping ${BASH_SOURCE[0]}.${reset}"
      return 0
   fi

   if [ "${dist_build_flavor:-}" = "whonix-custom-workstation" ]; then
      true "${cyan}INFO: Skipping creation of packages for custom workstation.${reset}"
   elif [ "${build_remote_derivative_pkgs:-}" = "true" ]; then
      true "${cyan}INFO: build_remote_derivative_pkgs is set to ${build_remote_derivative_pkgs}, skipping creation of packages.${reset}"
   else
      build_run_function create-debian-packages "$@"
   fi
}

build_run_function main "$@"
