#!/usr/bin/env bash # getrealdeps for Chromebrew version 1.1 # Author: Satadru Pramanik (satmandu) satadru at gmail dot com # set -x # export PS4='+(${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' pkg="${1%.rb}" # Exit quickly if an invalid package name is given. [[ ( -z "$pkg" ) || ( "$pkg" == *"#"* ) ]] && exit 1 if [ -z "${NOCOLORS}" ]; then RED='\e[1;91m'; # Use Light Red for errors. YELLOW='\e[1;33m'; # Use Yellow for informational messages. GREEN='\e[1;32m'; # Use Green for success messages. BLUE='\e[1;34m'; # Use Blue for intrafunction messages. GRAY='\e[0;37m'; # Use Gray for program output. MAGENTA='\e[1;35m'; RESET='\e[0m' else RED=''; YELLOW=''; GREEN=''; BLUE=''; GRAY=''; MAGENTA=''; RESET='' fi # Simplify colors and print errors to stderr (2). echo_error() { echo -e "${RED}${*}${RESET}" >&2; } echo_info() { echo -e "${YELLOW}${*}${RESET}" >&1; } echo_success() { echo -e "${GREEN}${*}${RESET}" >&1; } echo_intra() { echo -e "${BLUE}${*}${RESET}" >&1; } echo_out() { echo -e "${GRAY}${*}${RESET}" >&1; } echo_other() { echo -e "${MAGENTA}${*}${RESET}" >&2; } # Install jq if a functional local copy does not exist. if ! jq --version &> /dev/null; then crew install jq fi : "${CREW_PREFIX:=$(crew const CREW_PREFIX | cut -d= -f2)}" : "${CREW_LIB_PREFIX:=$(crew const CREW_LIB_PREFIX | cut -d= -f2)}" # Package needs to be installed for package filelist to be populated. if ! [[ $(jq --arg key "${pkg}" -e '.installed_packages[] | select(.name == $key )' "${CREW_PREFIX}"/etc/crew/device.json) ]]; then crew install "${pkg}" fi if ! [[ -f "${CREW_PREFIX}/etc/crew/meta/${pkg}.filelist" ]]; then echo_error "Package $pkg either does not exist or does not contain any libraries." exit 1 fi # Speed up grep export LC_ALL=C # Install grep if a functional local copy does not exist. if grep --version &> /dev/null; then GREP="grep" else crew install grep GREP="${CREW_PREFIX}/bin/grep" fi # Install mawk if a functional local copy of awk does not exist. if ! awk -W version &> /dev/null; then crew install mawk fi # Which packages have a needed library in CREW_LIB_PREFIX # This is a subset of what crew whatprovides gives whatprovidesfxn() { pkgdepslcl="${1}" # Handle patchelf inserted full library paths. if [[ "${pkgdepslcl}" == *"${CREW_LIB_PREFIX}"* ]]; then filelcl=$($GREP --exclude "${pkg}.filelist" --exclude={"${CREW_PREFIX}/etc/crew/meta/\*_build.filelist"} "${pkgdepslcl}$" "${CREW_PREFIX}"/etc/crew/meta/*.filelist) else filelcl=$($GREP --exclude "${pkg}.filelist" --exclude={"${CREW_PREFIX}/etc/crew/meta/\*_build.filelist"} "^${CREW_LIB_PREFIX}.*${pkgdepslcl}$" "${CREW_PREFIX}"/etc/crew/meta/*.filelist) fi packagelcl=$(echo "$filelcl" | \ sed 's/.filelist.*//g' | sed 's:.*/::' | awk '!x[$0]++' | sed s/://g) echo "$packagelcl" } # What files does a package provide crewfilesfxn() { pkgname="${1}" files=$(< "${CREW_PREFIX}"/etc/crew/meta/"${pkgname}".filelist) echo "$files" } pkgfiles=$(crewfilesfxn "${pkg}") # Use readelf to determine library dependencies, as # this doesn't almost run a program like using ldd would. rm -rf /tmp/deps/"${pkg}" pkgdeps=$(unset lines ; for i in $pkgfiles ; \ do \ [[ -f "${i}" ]] || continue read -r -n 4 exec_header_bytes < "${i}" [[ "$exec_header_bytes" == $'\x7FELF' ]] || continue [[ $i = *_tkinter* ]] && continue # Carveout for Python3 # Expand upx compressed binaries if in a container environment # since otherwise we cannot figure out their dependencies. [[ -f "/.dockerenv" ]] && upx -d "${i}" &> /dev/null mkdir -p /tmp/deps/"${pkg}"/ # shellcheck disable=SC2030,SC2046 lines+=$(echo ; readelf -d "$i" 2>/dev/null | $GREP NEEDED | awk '{print $5}' \ | sed 's/\[//g' | sed 's/\]//g' | awk '!x[$0]++' | tee /tmp/deps/"${pkg}"/$(basename "$i") ; echo ) ; \ done ; \ echo "$lines" | tr " " "\n" \ | awk 'NF' | awk '!x[$0]++') # Remove duplicates. pkgdeps="$(awk 'NF' <<< "${pkgdeps}" | awk '!x[$0]++')" # Figure out which Chromebrew packages provide the relevant deps. # shellcheck disable=SC2031 pkgdeps=$(unset lines; for j in $pkgdeps ; \ do lines+=$(echo ; whatprovidesfxn "$j"); done ; \ echo "$lines" | tr " " "\n" | awk '!x[$0]++') # Remove original package from list. pkgdeps=$(tr " " "\n" <<< "$pkgdeps" | sed "/${pkg}/d" | sort -u ) # Replace glibc_build packages with glibc pkgdeps=$(tr " " "\n" <<< "$pkgdeps" | sed 's/glibc_build.*/glibc/' | sort -u ) # Replace glibc_lib* packages with glibc_lib pkgdeps=$(tr " " "\n" <<< "$pkgdeps" | sed 's/glibc_lib.*/glibc_lib/' | sort -u ) # Note which dependencies are missing, but ignore :build lines, since # build depenencies may still be runtime dependencies. missingpkgdeps=$( for i in $pkgdeps do $GREP -v ":build" "${CREW_PREFIX}"/lib/crew/packages/"${pkg}".rb | $GREP -q "depends_on '$i'" || echo "$i" done ) echo_info "\nPackage ${pkg} has runtime library dependencies on these packages:" echo -e "${RESET}" for i in $pkgdeps do echo_intra " depends_on '$i' # R" done echo -e "${RESET}" # If there are no missing deps, no warning is needed. if [ -n "${missingpkgdeps}" ]; then echo_error "\nPackage file ${pkg}.rb is missing these runtime library dependencies:" echo -e "${RESET}" for i in $missingpkgdeps do echo_other " depends_on '$i' # R" done fi echo -e "${RESET}"