install: rewrite install script

* set dependency versions: PPA, git repository & commit
* separate download and build+install steps
* don't reinstall package if it's already installed

refs #4630

Change-Id: I966fac5e1633cbabf78ce20cd151a35618efc345
diff --git a/util/pkgdep/common.sh b/util/pkgdep/common.sh
new file mode 100644
index 0000000..9105fa2
--- /dev/null
+++ b/util/pkgdep/common.sh
@@ -0,0 +1,288 @@
+# -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2021, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+# needed by Python version detection logic in Mininet & Mininet-WiFi install script
+export PYTHON=python3
+
+# DEP_INFO key should match *_VERSION variable name.
+# DEP_INFO entry should have these comma separated fields:
+# [0] local directory name
+# [1] dep name
+# [2] PPA package name
+# [3] GitHub repository (HTTPS)
+# [4] Gerrit repository (repo name only)
+declare -A DEP_INFO
+DEP_INFO=(
+  ["CXX"]="ndn-cxx,ndn-cxx,libndn-cxx-dev,https://github.com/named-data/ndn-cxx.git,ndn-cxx"
+  ["NFD"]="NFD,NFD,nfd,https://github.com/named-data/NFD.git,NFD"
+  ["PSYNC"]="PSync,PSync,libpsync-dev,https://github.com/named-data/PSync.git,PSync"
+  ["NLSR"]="NLSR,NLSR,nlsr,https://github.com/named-data/NLSR.git,NLSR"
+  ["TOOLS"]="ndn-tools,NDN Essential Tools,ndn-tools,https://github.com/named-data/ndn-tools.git,ndn-tools"
+  ["TRAFFIC"]="ndn-traffic-generator,NDN Traffic Generator,ndn-traffic-generator,https://github.com/named-data/ndn-traffic-generator.git,ndn-traffic-generator"
+  ["INFOEDIT"]="infoedit,infoedit,,https://github.com/NDN-Routing/infoedit.git,"
+  ["MININET"]="mininet,Mininet,,https://github.com/mininet/mininet.git,"
+  ["MNWIFI"]="mininet-wifi,Mininet-WiFi,,https://github.com/intrig-unicamp/mininet-wifi.git,"
+)
+
+# command to detect dependency existence
+# if not specified, dependency is assumed to not exist
+declare -A DEP_DETECT
+DEP_DETECT=(
+  ["CXX"]="pkg-config libndn-cxx"
+  ["NFD"]="command -v nfd"
+  ["PSYNC"]="pkg-config PSync"
+  ["NLSR"]="command -v nlsr"
+  ["TOOLS"]="command -v ndnping && command -v ndncatchunks"
+  ["TRAFFIC"]="command -v ndn-traffic-client"
+  ["INFOEDIT"]="command -v infoedit"
+  ["MININET"]="command -v mn && command -v ofdatapath && $PYTHON -m pip show mininet"
+  ["MNWIFI"]="$PYTHON -m pip show mininet-wifi"
+)
+
+# non-Waf build & install command
+build_INFOEDIT() {
+  make -j${NJOBS}
+  $SUDO make install
+}
+build_MININET() {
+  ./util/install.sh -s ${CODEROOT} -nv
+  if ! command -v ofdatapath >/dev/null; then
+    rm -rf ${CODEROOT}/openflow
+    ./util/install.sh -s ${CODEROOT} -f
+  fi
+
+  $SUDO $PYTHON setup.py develop
+  $SUDO cp /usr/local/bin/mn /usr/local/bin/mn.mininet
+}
+build_MNWIFI() {
+  # issue: util/install.sh attempts to patch hostap every time, causing "Assume -R?" prompt
+  # during reinstalls
+  # workaround: revert hostap repository to clean state
+  $SUDO git -C hostap clean -fdx || true
+  $SUDO git -C hostap checkout -- . || true
+
+  # issue: util/install.sh is not using 'sudo' where needed
+  # solution: run whole script in 'sudo'
+  $SUDO env PYTHON="$PYTHON" ./util/install.sh -Wl
+
+  # issue: setup.py reports "Cannot load backend 'TkAgg' which requires the 'tk' interactive
+  # framework, as 'headless' is currently running" when running over SSH
+  # workaround: pretend to have X11 display
+  $SUDO env DISPLAY=:0 $PYTHON setup.py develop
+  $SUDO cp /usr/local/bin/mn /usr/local/bin/mn.wifi
+
+  # make 'mn' command refer to Mininet's version, which does not require X11
+  if [[ -x /usr/local/bin/mn.mininet ]]; then
+    $SUDO cp /usr/local/bin/mn.mininet /usr/local/bin/mn
+  fi
+}
+
+# Waf configure options
+declare -A DEP_WAFOPTS
+DEP_WAFOPTS=(
+  ["NFD"]="--without-websocket"
+  ["PSYNC"]="--with-examples"
+)
+
+# return whether dep is installed
+dep_exists() {
+  if [[ $IGNORE_EXISTING -eq 1 ]]; then
+    return 2
+  fi
+  local DETECT="${DEP_DETECT[$1]}"
+  if [[ -n $DETECT ]] && bash -c "$DETECT" &>/dev/null; then
+    return 0
+  fi
+  return 1
+}
+
+# set to 1 if dep needs downloading
+declare -A NEED_DL
+
+# display prompt on what to do with dep, populate NEED_DL
+dep_prompt() {
+  local INFO=()
+  IFS=',' read -a INFO <<< "${DEP_INFO[$1]}"
+  local VERSION_VAR="$1_VERSION"
+  local VERSION="${!VERSION_VAR}"
+  if [[ -z $VERSION ]]; then
+    VERSION='(default branch)'
+  fi
+  local DLDIR="${CODEROOT}/${INFO[0]}"
+  if dep_exists $1; then
+    echo "Will use existing ${INFO[1]}"
+  elif [[ $PREFER_FROM == ppa ]] && [[ -n ${INFO[2]} ]]; then
+    echo "Will install ${INFO[1]} from named-data PPA"
+    PPA_PKGS+=("${INFO[2]}")
+  elif [[ -d "$DLDIR" ]]; then
+    if [[ $DL_ONLY -ne 1 ]]; then
+      echo "Will install ${INFO[1]} from local checkout ${DLDIR}"
+    fi
+  else
+    NEED_DL[$1]=1
+    if [[ $DL_ONLY -eq 1 ]]; then
+      echo "Will checkout ${INFO[1]} ${VERSION} to ${DLDIR}"
+    else
+      echo "Will download and install ${INFO[1]} ${VERSION}"
+    fi
+  fi
+}
+
+# download dep source code
+dep_checkout() {
+  if [[ ${NEED_DL[$1]} -ne 1 ]]; then
+    return
+  fi
+  local INFO=()
+  IFS=',' read -a INFO <<< "${DEP_INFO[$1]}"
+  local VERSION_VAR="$1_VERSION"
+  local VERSION="${!VERSION_VAR}"
+  local DLDIR="${CODEROOT}/${INFO[0]}"
+
+  if [[ -n ${INFO[4]} ]] && [[ "$VERSION" =~ ^[0-9]+,[0-9]+$ ]]; then
+    local REPO="https://gerrit.named-data.net/${INFO[4]}"
+    local GERRIT_CHANGE=${VERSION%,*}
+    local GERRIT_PATCHSET=${VERSION#*,}
+    echo "Downloading ${INFO[2]} from Gerrit (patchset ${GERRIT_CHANGE},${GERRIT_PATCHSET})"
+    git clone "$REPO" "$DLDIR"
+    git -C "$DLDIR" fetch origin "refs/changes/${GERRIT_CHANGE:(-2)}/${GERRIT_CHANGE}/${GERRIT_PATCHSET}"
+    git -C "$DLDIR" -c advice.detachedHead=false checkout FETCH_HEAD
+  elif [[ -z $VERSION ]]; then
+    echo "Downloading ${INFO[1]} from GitHub (default branch)"
+    git clone "${INFO[3]}" "$DLDIR"
+  elif [[ "$VERSION" =~ @ ]]; then
+    local REPO=${VERSION%@*}
+    local BRANCH=${VERSION#*@}
+    echo "Downloading ${INFO[1]} from ${REPO} (branch ${BRANCH})"
+    git clone "$REPO" "$DLDIR"
+    git -C "$DLDIR" -c advice.detachedHead=false checkout "$BRANCH"
+  else
+    echo "Downloading ${INFO[1]} from GitHub (branch ${VERSION})"
+    git clone "${INFO[3]}" "$DLDIR"
+    git -C "$DLDIR" -c advice.detachedHead=false checkout "$VERSION"
+  fi
+  git submodule update --init --recursive --progress
+}
+
+# install dep from source code
+dep_install() {
+  local INFO=()
+  IFS=',' read -a INFO <<< "${DEP_INFO[$1]}"
+  local DLDIR="${CODEROOT}/${INFO[0]}"
+  if dep_exists $1 || ! [[ -d "$DLDIR" ]]; then
+    return
+  fi
+  pushd "$DLDIR"
+
+  local FN="build_$1"
+  if declare -F $FN >/dev/null; then
+    echo "Installing ${INFO[1]} with custom command"
+    $FN
+  elif [[ -x waf ]]; then
+    echo "Installing ${INFO[1]} with Waf"
+    $PYTHON ./waf configure ${DEP_WAFOPTS[$1]}
+    $PYTHON ./waf -j${NJOBS}
+    $SUDO $PYTHON ./waf install
+  fi
+  popd
+}
+
+if [[ $PPA_AVAIL -ne 1 ]] || [[ $NO_PPA -eq 1 ]]; then
+  PREFER_FROM=source
+fi
+
+# dep install order, excluding ndn-cxx
+DEPLIST=(NFD PSYNC NLSR TOOLS TRAFFIC INFOEDIT MININET)
+if [[ $NO_WIFI -ne 1 ]]; then
+  DEPLIST+=(MNWIFI)
+fi
+
+echo "Will download to ${CODEROOT}"
+echo 'Will install compiler and build tools'
+dep_prompt CXX
+if [[ $CXX_DUMMY_KEYCHAIN -eq 1 ]]; then
+  echo 'Will patch ndn-cxx to use dummy KeyChain'
+fi
+for DEP in "${DEPLIST[@]}"; do
+  dep_prompt $DEP
+done
+if [[ $DL_ONLY -ne 1 ]]; then
+  echo "Will compile with ${NJOBS} parallel jobs"
+  echo "Will install Mini-NDN package"
+fi
+
+if [[ $CONFIRM -ne 1 ]]; then
+  read -p 'Press ENTER to continue or CTRL+C to abort '
+fi
+
+install_pkgs
+
+if [[ -z $SKIPPYTHONCHECK ]]; then
+  PYTHON_VERSION=$($PYTHON --version)
+  SUDO_PYTHON_VERSION=$($SUDO $PYTHON --version)
+  if [[ "$PYTHON_VERSION" != "$SUDO_PYTHON_VERSION" ]]; then
+    cat <<EOT
+In your system, '${PYTHON}' is ${PYTHON_VERSION} and '$SUDO ${PYTHON}' is ${SUDO_PYTHON_VERSION}
+You must manually resolve the conflict, e.g. delete excess Python installation or change $PATH
+To bypass this check, set the environment variable SKIPPYTHONCHECK=1
+EOT
+    exit 1
+  fi
+fi
+
+if ! mkdir -p "${CODEROOT}" 2>/dev/null; then
+  $SUDO mkdir -p "${CODEROOT}"
+  $SUDO chown $(id -u) "${CODEROOT}"
+fi
+dep_checkout CXX
+if [[ $CXX_DUMMY_KEYCHAIN -eq 1 ]]; then
+  echo 'Patching ndn-cxx to use dummy KeyChain'
+  if ! git -C "${CODEROOT}/ndn-cxx" apply --index < "${PKGDEPDIR}/../patches/ndn-cxx-dummy-keychain.patch"; then
+    echo 'Cannot patch ndn-cxx to use dummy KeyChain (possibly already patched)'
+  fi
+fi
+for DEP in "${DEPLIST[@]}"; do
+  dep_checkout $DEP
+done
+
+if [[ $DL_ONLY -eq 1 ]]; then
+  cat <<EOT
+Source code has been downloaded to ${CODEROOT}
+You may make changes or checkout different versions
+Run this script again without --dl-only to install from local checkout
+EOT
+  exit 0
+fi
+
+prepare_ld
+for DEP in CXX "${DEPLIST[@]}"; do
+  dep_install $DEP
+done
+$SUDO ldconfig
+
+DESTDIR=/usr/local/etc/mini-ndn
+$SUDO install -d -m0755 "$DESTDIR"
+find topologies/ -name '*.conf' | xargs $SUDO install -m0644 -t "$DESTDIR/"
+$SUDO $PYTHON setup.py develop
+
+echo 'MiniNDN installation completed successfully'
diff --git a/util/pkgdep/debian-like.sh b/util/pkgdep/debian-like.sh
new file mode 100644
index 0000000..52fb9a5
--- /dev/null
+++ b/util/pkgdep/debian-like.sh
@@ -0,0 +1,66 @@
+# -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2021, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+# APT packages, written in alphabetical order.
+APT_PKGS=(
+  build-essential
+  ca-certificates
+  git
+  libboost-atomic-dev
+  libboost-chrono-dev
+  libboost-date-time-dev
+  libboost-filesystem-dev
+  libboost-iostreams-dev
+  libboost-log-dev
+  libboost-program-options-dev
+  libboost-regex-dev
+  libboost-stacktrace-dev
+  libboost-system-dev
+  libboost-thread-dev
+  libpcap-dev
+  libsqlite3-dev
+  libssl-dev
+  libsystemd-dev
+  pkg-config
+  python-is-python3
+  python3-pip
+  software-properties-common
+  tshark
+)
+
+if [[ $NO_WIFI -ne 1 ]]; then
+  APT_PKGS+=(network-manager)
+fi
+
+install_pkgs() {
+  $SUDO apt-get update
+  $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${APT_PKGS[@]}"
+  if [[ $PPA_AVAIL -eq 1 ]] && [[ ${#PPA_PKGS[@]} -gt 0 ]]; then
+    $SUDO add-apt-repository -y -u ppa:named-data/ppa
+    $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${PPA_PKGS[@]}"
+  fi
+}
+
+prepare_ld() {
+  return
+}
diff --git a/util/pkgdep/debian.sh b/util/pkgdep/debian.sh
new file mode 100644
index 0000000..3496e68
--- /dev/null
+++ b/util/pkgdep/debian.sh
@@ -0,0 +1,35 @@
+# -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2021, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+if [[ $VERSION_ID -lt 11 ]]; then
+  cat <<EOT
+Debian 11 or newer is required
+Installation on older versions may fail
+EOT
+fi
+
+source "$PKGDEPDIR/debian-like.sh"
+
+APT_PKGS+=(
+  libigraph-dev
+)
diff --git a/util/pkgdep/fedora.sh b/util/pkgdep/fedora.sh
new file mode 100644
index 0000000..b48dac2
--- /dev/null
+++ b/util/pkgdep/fedora.sh
@@ -0,0 +1,57 @@
+# -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2021, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+if [[ $VERSION_ID -lt 33 ]]; then
+  cat <<EOT
+Fedora 33 or newer is required
+Installation on older versions may fail
+EOT
+fi
+
+# DNF packages, written in alphabetical order.
+DNF_PKGS=(
+  boost-devel
+  ca-certificates
+  gcc-c++
+  libpcap-devel
+  openssl-devel
+  python3-pip
+  sqlite-devel
+  systemd-devel
+)
+
+install_pkgs() {
+  $SUDO dnf install -y "${DNF_PKGS[@]}"
+}
+
+prepare_ld() {
+  if ! $SUDO ldconfig -v 2>/dev/null | grep -q /usr/local/lib64; then 
+    echo 'Enabling /usr/local/lib64 in dynamic linker'
+    $SUDO mkdir -p /etc/ld.so.conf.d
+    echo /usr/local/lib64 | $SUDO tee /etc/ld.so.conf.d/usr-local-lib64.conf >/dev/null
+  fi
+}
+
+# ndn-cxx installs to /usr/local/lib64, but `pkg-config --libs libndn-cxx` by default strips out
+# `-L/usr/local/lib64` flag. This environ or `pkg-config --keep-system-libs` prevents that.
+export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
diff --git a/util/pkgdep/ubuntu.sh b/util/pkgdep/ubuntu.sh
new file mode 100644
index 0000000..91abc29
--- /dev/null
+++ b/util/pkgdep/ubuntu.sh
@@ -0,0 +1,37 @@
+# -*- Mode:bash; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2015-2021, The University of Memphis,
+#                          Arizona Board of Regents,
+#                          Regents of the University of California.
+#
+# This file is part of Mini-NDN.
+# See AUTHORS.md for a complete list of Mini-NDN authors and contributors.
+#
+# Mini-NDN is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mini-NDN is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Mini-NDN, e.g., in COPYING.md file.
+# If not, see <http://www.gnu.org/licenses/>.
+
+if [[ $(expr $VERSION_ID '<' 20.04) == 1 ]]; then
+  cat <<EOT
+Ubuntu 20.04 or newer is required
+Installation on older versions may fail
+EOT
+fi
+
+source "$PKGDEPDIR/debian-like.sh"
+
+APT_PKGS+=(
+  libigraph0-dev
+)
+
+PPA_AVAIL=1