#!/bin/bash # Author: Alpha Eva # # Notes: ServerStack for Debian 9-13 and Ubuntu 16-26 (Ubuntu/Debian only) # # Project home page: # https://oneinstack.com # https://github.com/oneinstack/oneinstack # Filter out packages that do not exist in the configured apt sources. # A single unavailable package name would otherwise abort the whole # 'apt-get install' and leave every dependency (libsqlite3-dev, libssl-dev, # libxml2-dev, ...) uninstalled -- which is exactly what breaks PHP's configure # (the "Package 'sqlite3' ... not found" error). Modern Debian/Ubuntu removed # several legacy package names (libjpeg8, libpng12-*, libcloog-ppl1, libidn11, # ...) that are still listed below, so this guard keeps the install resilient. filter_pkglist() { local _avail="" _p for _p in ${pkgList}; do # Use `apt-cache policy` instead of `apt-cache show`: the latter returns a # non-zero exit for *purely virtual* packages (e.g. libpng-dev, libjpeg-dev # on modern Debian/Ubuntu), which would wrongly drop them and leave GD's # libpng/libjpeg .pc files missing. `apt-cache policy` resolves virtual # packages to their provider and reports a real candidate version. if apt-cache policy "${_p}" 2>/dev/null | grep -qE 'Candidate: [0-9]'; then _avail="${_avail} ${_p}" else echo "${CWARNING}Skip unavailable package: ${_p}${CEND}" fi done pkgList="${_avail}" # Resolve known MUTUAL conflicts inside the list. Unlike missing packages # (handled above), these two packages both exist in apt but declare # `Conflicts:` on each other, so a single `apt-get install A B` aborts with # "Unable to correct problems, you have held broken packages" and leaves # EVERY other dependency uninstalled -- which then breaks PHP's ./configure. # # libcurl4-gnutls-dev <-> libcurl4-openssl-dev # Both provide libcurl4 headers under different TLS backends and are # mutually exclusive on every supported Ubuntu/Debian release. All PHP # build modules in this tree (modules/php/php-*.sh) link against the # OpenSSL flavor, so we keep libcurl4-openssl-dev and drop the gnutls dev # package. The non-dev runtime `libcurl3-gnutls` is NOT touched. if echo "${pkgList}" | grep -qw 'libcurl4-gnutls-dev' \ && echo "${pkgList}" | grep -qw 'libcurl4-openssl-dev'; then pkgList="$(echo "${pkgList}" | sed -E 's/\blibcurl4-gnutls-dev\b//')" echo "${CWARNING}Resolved libcurl dev conflict: dropped libcurl4-gnutls-dev, kept libcurl4-openssl-dev${CEND}" fi } # Install the (already filtered) dependency packages. # 'apt-get install' is transactional: if any single package in the list fails to # install (dependency conflict, a pinned version that no longer exists, a flaky # download, ...), apt rolls back the WHOLE operation and leaves every -dev # package uninstalled. That in turn breaks PHP's ./configure (missing .pc files # like sqlite3 / libcurl / libxml2). To stay resilient we try the fast batch # install first and, on failure, fall back to installing each package on its own # so one bad package can no longer block the rest. # # Why this used to "hang for a long time" at the libcurl conflict: # 1. When the batch `apt-get install` is given two mutually-conflicting # packages (libcurl4-gnutls-dev + libcurl4-openssl-dev), apt's dependency # solver silently spends 30s~2min trying alternative resolutions before # finally printing "Unable to correct problems, you have held broken # packages" and returning non-zero. During that time there is NO output, # so it looks like the script is frozen. # 2. After the batch fails, the old fallback loop ran `apt-get install` once # per package (~60 packages) with ALL output redirected to /dev/null, so # the user saw a blank screen for several more minutes. # Both are fixed below: (a) the known mutual conflict is stripped before apt # is ever called, and (b) apt now runs non-interactively with its verbose # output appended to runtime/install.log while the screen only shows concise # status lines, so the flow is neither silent nor noisy. install_pkgs() { # Defense in depth: strip the known libcurl dev conflict here too, in case # filter_pkglist() was bypassed or pkgList was mutated between filter and # install. This guarantees the batch install below cannot abort on that # specific conflict (which is the #1 cause of the long "hang"). if echo "${pkgList}" | grep -qw 'libcurl4-gnutls-dev' \ && echo "${pkgList}" | grep -qw 'libcurl4-openssl-dev'; then pkgList="$(echo "${pkgList}" | sed -E 's/\blibcurl4-gnutls-dev\b//')" echo "${CWARNING}Resolved libcurl dev conflict: dropped libcurl4-gnutls-dev, kept libcurl4-openssl-dev${CEND}" fi # apt runs fully non-interactively (-y + DEBIAN_FRONTEND=noninteractive) so it # never stops on a debconf prompt (a common cause of the flow "hanging"). The # verbose apt download/configure output is NOT dumped to the terminal; instead # it is appended to runtime/install.log, and the screen shows only concise # status lines. This keeps the console readable while preserving a complete, # greppable install log for troubleshooting. local _log="${oneinstack_dir:-.}/runtime/install.log" export DEBIAN_FRONTEND=noninteractive # Fast path: try to install everything in one apt invocation. This is by far # the fastest when there are no conflicts, because apt resolves the whole # dependency graph once instead of N times. echo "${CMSG}Installing dependency packages (apt-get -y, full output -> ${_log}) ...${CEND}" if apt-get --no-install-recommends -y install ${pkgList} >> "${_log}" 2>&1; then echo "${CSUCCESS}All dependency packages installed successfully.${CEND}" return 0 fi # Slow path: batch failed for some reason (unexpected conflict, flaky mirror, # ...). Install each package individually so one bad package cannot block the # rest. Each package still installs silently via apt-get -y (output appended # to the log), but a per-package status line is printed so the screen is never # blank and every package's outcome (OK / FAILED) is explicit. echo "${CWARNING}Batch dependency install failed, retrying package-by-package (details -> ${_log}) ...${CEND}" local _p for _p in ${pkgList}; do printf '%s -> %-40s ...%s' "${CMSG}" "${_p}" "${CEND}" if apt-get --no-install-recommends -y install "${_p}" >> "${_log}" 2>&1; then printf ' %sOK%s\n' "${CSUCCESS}" "${CEND}" else printf ' %sFAILED%s\n' "${CFAILURE}" "${CEND}" fi done } installDepsDebian() { echo "${CMSG}Removing the conflicting packages...${CEND}" if [[ "${db_option}" =~ ^[1-9]$|^1[0-2]$ ]]; then pkgList="mysql-client mysql-server mysql-common mysql-server-core-5.5 mysql-client-5.5 mariadb-client mariadb-server mariadb-common" for Package in ${pkgList};do apt-get -y purge ${Package} done dpkg -l | grep ^rc | awk '{print $2}' | xargs dpkg -P fi echo "${CMSG}Installing dependencies packages...${CEND}" apt-get -y update apt-get -y autoremove apt-get -yf install export DEBIAN_FRONTEND=noninteractive # critical security updates grep security /etc/apt/sources.list > /tmp/security.sources.list apt-get -y upgrade -o Dir::Etc::SourceList=/tmp/security.sources.list # Install needed packages case "${Debian_ver}" in 8) pkgList="debian-keyring debian-archive-keyring build-essential gcc g++ make cmake autoconf libjpeg8 libjpeg62-turbo-dev libjpeg-dev libpng12-0 libpng12-dev libpng3 libgd-dev libxml2 libxml2-dev zlib1g zlib1g-dev libc6 libc6-dev libc-client2007e-dev libglib2.0-0 libglib2.0-dev bzip2 libzip-dev libbz2-1.0 libncurses5 libncurses5-dev libaio1 libaio-dev numactl libreadline-dev curl libcurl3-gnutls libcurl4-openssl-dev e2fsprogs libkrb5-3 libkrb5-dev libltdl-dev libidn11 libidn11-dev openssl net-tools libssl-dev libtool libevent-dev bison re2c libsasl2-dev libxslt1-dev libxslt-dev libicu-dev locales libcloog-ppl0 patch vim zip unzip tmux htop bc dc expect libexpat1-dev libonig-dev libtirpc-dev nss rsync git lsof lrzsz iptables rsyslog cron logrotate chrony ntpdate libsqlite3-dev psmisc wget sysv-rc apt-transport-https ca-certificates software-properties-common gnupg" ;; # Debian 13 (trixie) reuses the Debian 12 list. Any package-name drift # versus 12 (e.g. libidn11 removal in favour of libidn2) is automatically # absorbed by filter_pkglist(), which drops packages with no apt candidate # instead of failing the whole install. 9|10|11|12|13) pkgList="debian-keyring debian-archive-keyring build-essential gcc g++ make cmake autoconf libjpeg62-turbo-dev libjpeg-dev libpng-dev libgd-dev libxml2 libxml2-dev zlib1g zlib1g-dev libc6 libc6-dev libc-client2007e-dev libglib2.0-0 libglib2.0-dev bzip2 libzip-dev libbz2-1.0 libncurses5 libncurses5-dev libaio1 libaio-dev numactl libreadline-dev curl libcurl3-gnutls libcurl4-openssl-dev e2fsprogs libkrb5-3 libkrb5-dev libltdl-dev libidn11 libidn11-dev openssl net-tools libssl-dev libtool libevent-dev bison re2c libsasl2-dev libxslt1-dev libicu-dev locales patch vim zip unzip tmux htop bc dc expect libexpat1-dev libonig-dev libtirpc-dev rsync git lsof lrzsz iptables rsyslog cron logrotate chrony ntpdate libsqlite3-dev psmisc wget sysv-rc apt-transport-https ca-certificates software-properties-common gnupg" ;; *) echo "${CFAILURE}Your system Debian ${Debian_ver} are not supported!${CEND}" kill -9 $$; exit 1; ;; esac # Install all dependency packages in a single apt invocation (fast path); # install_pkgs() falls back to per-package on failure so one bad package # cannot block the rest. filter_pkglist install_pkgs } installDepsUbuntu() { # Uninstall the conflicting software echo "${CMSG}Removing the conflicting packages...${CEND}" if [[ "${db_option}" =~ ^[1-9]$|^1[0-2]$ ]]; then pkgList="mysql-client mysql-server mysql-common mysql-server-core-5.5 mysql-client-5.5 mariadb-client mariadb-server mariadb-common" for Package in ${pkgList};do apt-get -y purge ${Package} done dpkg -l | grep ^rc | awk '{print $2}' | xargs dpkg -P fi echo "${CMSG}Installing dependencies packages...${CEND}" apt-get -y update apt-get -y autoremove apt-get -yf install export DEBIAN_FRONTEND=noninteractive [[ "${Ubuntu_ver}" =~ ^22$ ]] && apt-get -y --allow-downgrades install libicu70=70.1-2 libglib2.0-0=2.72.1-1 libxml2-dev # critical security updates grep security /etc/apt/sources.list > /tmp/security.sources.list apt-get -y upgrade -o Dir::Etc::SourceList=/tmp/security.sources.list # Install needed packages # NOTE on the libcurl entries: # * `libcurl3-gnutls` is the shared runtime (non-dev) and is harmless to keep. # * `libcurl4-gnutls-dev` and `libcurl4-openssl-dev` are MUTUALLY EXCLUSIVE on # every supported Ubuntu release: they provide competing flavors of # libcurl4 headers and the two -dev packages declare a Conflicts: on each # other. Listing both in a single apt invocation makes # `apt-get install` abort with # "libcurl4-gnutls-dev : Conflicts: libcurl4-openssl-dev ..." # and leaves every other dependency uninstalled, which then breaks # PHP's ./configure (missing libcurl.pc, libxml2, sqlite3, ...). # * All PHP build modules (modules/php/php-*.sh) and every other consumer # in this tree link against the OpenSSL flavor, so we keep # `libcurl4-openssl-dev` and drop the gnutls dev package here. # The following legacy names are also removed from this Ubuntu list because # they have been dropped from current Ubuntu releases and are now permanently # filtered out by filter_pkglist (which still leaves them as noisy "Skip # unavailable" warnings on every run): # libjpeg62-turbo-dev -- only present up through 22.04 # libpng12-0 / libpng12-dev / libpng3 -- superseded by libpng16-16 / libpng-dev # libidn11 -- superseded by libidn2-0 # libcloog-ppl1 -- removed since gcc dropped PPL/CLooG # sysv-rc -- replaced by systemd pkgList="libperl-dev debian-keyring debian-archive-keyring build-essential gcc g++ make cmake autoconf libjpeg-dev libpng-dev libxml2 libxml2-dev zlib1g zlib1g-dev libc6 libc6-dev libc-client2007e-dev libglib2.0-0 libglib2.0-dev bzip2 libzip-dev libbz2-1.0 libncurses5 libncurses5-dev libaio1 libaio-dev numactl libreadline-dev curl libcurl3-gnutls libcurl4-openssl-dev e2fsprogs libkrb5-3 libkrb5-dev libltdl-dev libidn11-dev openssl net-tools libssl-dev libtool libevent-dev re2c libsasl2-dev libxslt1-dev libicu-dev libsqlite3-dev bison patch vim zip unzip tmux htop bc dc expect libexpat1-dev iptables rsyslog libonig-dev libtirpc-dev libnss3 rsync git lsof lrzsz chrony ntpdate psmisc wget apt-transport-https ca-certificates software-properties-common gnupg" export DEBIAN_FRONTEND=noninteractive # Install all dependency packages in a single apt invocation (fast path); # install_pkgs() falls back to per-package on failure so one bad package # cannot block the rest. filter_pkglist install_pkgs } installDepsBySrc() { pushd ${oneinstack_dir}/src > /dev/null if ! command -v icu-config > /dev/null 2>&1 || icu-config --version | grep '^3.' || [ "${Ubuntu_ver}" == "20" ]; then echo "${CMSG}>> Installing: ICU ${icu4c_ver} (compiling from source)${CEND}" tar xzf icu4c-${icu4c_ver}-src.tgz pushd icu/source > /dev/null ./configure --prefix=/usr/local make -j ${THREAD} && make install popd > /dev/null rm -rf icu fi if command -v lsof >/dev/null 2>&1; then echo 'already initialize' > ~/.oneinstack else echo "${CFAILURE}${PM} config error parsing file failed${CEND}" kill -9 $$; exit 1; fi popd > /dev/null }