#!/bin/bash # check for packages that need to be built, and build a list in the proper build order # Details: # https://github.com/archlinux32/builder/wiki/Build-system#get-package-updates # TODO: # include repository of package customizations . "${0%/*}/../conf/default.conf" # Create a lock file for build list. exec 9> "${lock_file}" flock -n 9 || exit # Update git repositories (official packages, community packages and the repository of package customizations). for repo in "${repo_paths[@]}"; do git -C "${repo}" clean -df git -C "${repo}" checkout master git -C "${repo}" pull done # Read previous git revision numbers from files. declare -A old_repo_revisions for repo in "${!repo_paths[@]}"; do old_repo_revisions["${repo}"]="$( cat "${work_dir}/${repo}.revision" 2> /dev/null || \ echo NONE )" git -C "${repo_paths["${repo}"]}" rev-parse HEAD > \ "${work_dir}/${repo}.revision.new" done echo 'Check modified packages from the last update, and put them to the build list.' # Check modified packages from the last update, and put them to the build list. # If a package is updated, but already on the rebuild list, then just update the git revision number. # If a package is deleted, remove from the rebuild list, and add it to the deletion list. # If a new package is added, then ensure that it's not on the deletion list. cp "${work_dir}/build-list"{,.new} cp "${work_dir}/deletion-list"{,.new} ( for repo in "${!repo_paths[@]}"; do current_HEAD="$(cat "${work_dir}/${repo}.revision.new")" ( # if old revision unknown, mimic "git diff"-output if [ "${old_repo_revisions["${repo}"]}" == "NONE" ]; then git -C "${repo_paths["${repo}"]}" archive --format=tar HEAD | \ tar -t | \ sed 's|^|A\t|' else git -C "${repo_paths["${repo}"]}" diff --no-renames --name-status "${old_repo_revisions["${repo}"]}" HEAD fi ) | \ # only track changes in PKGBUILDs grep '^.\s[^/]\+/repos/[^/]\+/PKGBUILD$' | \ # ignore i686 grep -v -- '-i686/PKGBUILD$' | \ sed 's|^\(.\)\t\([^/]\+\)/repos/\([^/]\+\)-[^/-]\+/PKGBUILD$|\1 \2 '"${current_HEAD}"' \3|' | \ # ignore staging and testing grep -v '\(staging\|testing\)$' done | \ sort -u # ignore blacklisted packages by explicitely deleting them _at_the_end_ sed "s|^\(.*\)\$|D \1 $(cat "${work_dir}/archlinux32.revision.new") archlinux32|" "${repo_paths["archlinux32"]}/blacklist" ) | \ while read -r mode package git_revision repository; do case "${mode}" in # new or modified PKGBUILD "A"|"M") sed -i "/^${package} /d" "${work_dir}/build-list" echo "${package} ${git_revision} ${repository}" >> \ "${work_dir}/build-list.new" sed -i "/^${package}\$/d" "${work_dir}/deletion-list.new" ;; # deleted PKGBUILD "D") echo "${package}" >> \ "${work_dir}/deletion-list.new" sed -i "/^${package} /d" "${work_dir}/build-list.new" ;; *) >&2 echo "unknown git diff mode '${mode}'" exit 1 ;; esac done echo 'Extract dependencies of packages.' # Put the list in the proper build order. # First, we extract the dependencies of each package. # TODO: # package modifications need to be included here mkdir -p "${work_dir}/package-infos" rm -f "${work_dir}/build-order" touch "${work_dir}/build-order" while read -r package git_revision repository; do PKGBUILD="$(find_pkgbuild "${package}" "${repository}")" if [ ! -r "${PKGBUILD}" ]; then echo "can't find PKGBUILD to package '${package}' from repository '${repository}': '${PKGBUILD}'" exit 1 fi # delete cached values of old versions of this PKGBUILD ls -1 "${work_dir}/package-infos/${package}-"* 2> /dev/null | \ sed 's|^.*/||' | \ grep "^${package}"'\.[0-9a-f]\{40\}\.\(builds\|needs\)$' | \ grep -v "^${package}\.${git_revision}"'\.\(builds\|needs\)$' | \ while read file; do rm "${work_dir}/package-infos/${file}" done # extract infos from PKGBUILD if not existent yet if [ ! -e "${work_dir}/package-infos/${package}.${git_revision}.builds" ] || \ [ ! -e "${work_dir}/package-infos/${package}.${git_revision}.needs" ]; then ( cd "${PKGBUILD%/*}" mksrcinfo -o "${work_dir}/package-infos/${package}.${git_revision}.SRCINFO" ) # extract "builds" = provides \cup pkgname grep '^\('$'\t''provides\|pkgname\) = ' "${work_dir}/package-infos/${package}.${git_revision}.SRCINFO" | \ cut -d= -f2 | \ sed 's|^\s\+||; s|[<>]$||' | \ sort -u > \ "${work_dir}/package-infos/${package}.${git_revision}.builds" # extract "needs" = ( makedepends \cup checkdepends ) \setminus "builds" ( ( # this would include runtime dependencies, too: # sed -n '/^pkgname = /q;/^'$'\t''depends = /p' "${work_dir}/package-infos/${package}.${git_revision}.SRCINFO" grep '^'$'\t''\(makedepends\|checkdepends\) = ' "${work_dir}/package-infos/${package}.${git_revision}.SRCINFO" ) | \ cut -d= -f2 | \ sed 's|^\s\+||; s|[<>]$||' | \ sort -u cat "${work_dir}/package-infos/${package}.${git_revision}.builds" | \ sed 'p' ) | \ sort | \ uniq -u > \ "${work_dir}/package-infos/${package}.${git_revision}.needs" rm "${work_dir}/package-infos/${package}.${git_revision}.SRCINFO" fi # add "$pkgname -> $build-target" to build-order list while read target; do echo "${package} ${target}" >> \ "${work_dir}/build-order" done < "${work_dir}/package-infos/${package}.${git_revision}.builds" # add "$dependency -> $pkgname" to build-order list while read dependency; do echo "${dependency} ${package}" >> \ "${work_dir}/build-order" done < "${work_dir}/package-infos/${package}.${git_revision}.needs" done < "${work_dir}/build-list.new" echo 'Now actually sort it.' ( # this part will have the correct build order, but all the infos are missing tsort "${work_dir}/build-order" 2> "${work_dir}/tsort.error" | \ nl -ba | \ awk '{print $1 " not-git whatever " $2}' # this part has all the infos, but possibly the wrong order awk '{print "0 " $2 " " $3 " " $1}' < \ "${work_dir}/build-list.new" ) | \ sort -k4,4 -k1nr | \ # now, we have the correct order and the infos, but in adjacent lines uniq -f3 -D | \ sed '/^0 /d;N;s|\n| |' | \ # now in one line, each sort -k1n,1 | \ awk '{print $4 " " $6 " " $7}' > \ "${work_dir}/build-list.new.new" rm -f "${work_dir}/build-order.loops/*" if [ -s "${work_dir}/tsort.error" ]; then >&2 echo 'WARNING: There is a dependency cycle!' >&2 cat "${work_dir}/tsort.error" >&2 echo >&2 echo 'I will continue anyway.' # save loops in separate files each, so breaking them is easier awk ' /^tsort: \S+: input contains a loop:$/{ n++; getline } { print $2 >"'"${work_dir}"'/build-list.loops/loop_" n } ' "${work_dir}/tsort.error" else rm "${work_dir}/tsort.error" fi # Move the .new-files to the actual files ( echo "build-list.new" "build-list" "deletion-list" echo "${!repo_paths[@]}" | \ sed 's@\( \|$\)@.revision\1@g' ) | \ tr ' ' '\n' | \ while read -r file; do mv "${work_dir}/${file}.new" "${work_dir}/${file}" done # Remove the lock file rm -f "${lock_file}"