Install script: fall back to su without sudo, add --build-only

Change-Id: Ibfbaef4ee01a35a0bd8f430d423746f25939b10c
diff --git a/README.md b/README.md
index 6229bd8..0f1961e 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,16 @@
 `scripts/install.sh --help` for the options, e.g. `--shared` for linking
 rocksdb and collocatordb shared, or `--prefix` for another install location.
 
+Where root rights are hard to come by - e.g. a server where you have only the
+root password and no sudo - the script falls back to `su`, which prompts for
+that password. And with `--build-only` it needs no root at all: it builds
+everything, tests it, and leaves the binaries in the build directory, linked
+against the static collocatordb and rocksdb, so they run from there:
+
+```bash
+scripts/install.sh --build-only
+```
+
 ### The four steps, one at a time
 
 What the script runs, for doing it by hand or where it does not reach:
diff --git a/scripts/build-rocksdb.sh b/scripts/build-rocksdb.sh
index cc2cd1f..fca1e44 100755
--- a/scripts/build-rocksdb.sh
+++ b/scripts/build-rocksdb.sh
@@ -78,7 +78,7 @@
 if [ -n "$missing" ]; then
     echo "Missing build tools:$missing" >&2
     echo >&2
-    echo "Install them and the compression libraries:" >&2
+    echo "Install them and the compression libraries, with sudo or su -c:" >&2
     echo "  Fedora, Rocky, RHEL: sudo dnf install cmake gcc-c++ git snappy-devel zlib-devel bzip2-devel lz4-devel libzstd-devel" >&2
     echo "  Debian, Ubuntu:      sudo apt-get install cmake g++ git libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev" >&2
     echo "  MacOS:               brew install cmake snappy zlib bzip2 lz4 zstd" >&2
@@ -151,4 +151,5 @@
     echo
     echo "Programs that use the shared library find it after:"
     echo "  echo $PREFIX/$LIBDIR | sudo tee /etc/ld.so.conf.d/rocksdb.conf && sudo ldconfig"
+    echo "  (or 'su -c \"...\"' where there is no sudo)"
 fi
diff --git a/scripts/install.sh b/scripts/install.sh
index bb076ea..33a2a22 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -19,6 +19,9 @@
 #                          distribution
 #   --skip-deps            do not install the packages of the distribution
 #   --skip-tests           do not run ctest
+#   --build-only           build everything but install nothing: no packages of
+#                          the distribution, no root needed, the binaries stay
+#                          in the build directory
 #   --static-binary        build dereko2vec as a completely static binary
 #                          (needs static compression libraries, which Debian
 #                          and Ubuntu ship and Fedora, Rocky and RHEL do not)
@@ -34,6 +37,7 @@
 STATIC_BINARY=0
 SKIP_DEPS=0
 SKIP_TESTS=0
+BUILD_ONLY=0
 JOBS=
 
 usage() {
@@ -50,12 +54,18 @@
         --rocksdb-version)  ROCKSDB_VERSION=$2; shift 2 ;;
         --skip-deps)        SKIP_DEPS=1; shift ;;
         --skip-tests)       SKIP_TESTS=1; shift ;;
+        --build-only)       BUILD_ONLY=1; shift ;;
         -j)                 JOBS=$2; shift 2 ;;
         -h|--help)          usage 0 ;;
         *) echo "Unknown option: $1" >&2; usage 1 ;;
     esac
 done
 
+# Nothing is installed, so nothing of the distribution is needed either
+if [ $BUILD_ONLY = 1 ]; then
+    SKIP_DEPS=1
+fi
+
 if [ -z "$JOBS" ]; then
     JOBS=$(getconf _NPROCESSORS_ONLN 2> /dev/null || sysctl -n hw.ncpu)
 fi
@@ -63,13 +73,28 @@
 
 step() { printf '\n=== %s ===\n' "$*"; }
 
-# sudo only where the target is not writable
-if [ -w "$PREFIX" ] || { [ ! -e "$PREFIX" ] && [ -w "$(dirname "$PREFIX")" ]; }; then
-    SUDO=
-else
-    SUDO=sudo
+# Root is needed only where the target is not writable
+NEED_ROOT=
+if ! [ -w "$PREFIX" ] && ! { [ ! -e "$PREFIX" ] && [ -w "$(dirname "$PREFIX")" ]; }; then
+    NEED_ROOT=1
 fi
 
+# Runs a command as root. Prefer sudo, fall back to su, which prompts for the
+# root password - a server may well have only that.
+root() {
+    if [ -z "$NEED_ROOT" ] || [ "$(id -u)" = 0 ]; then
+        "$@"
+    elif command -v sudo > /dev/null; then
+        sudo "$@" || su -c "$*"
+    elif command -v su > /dev/null; then
+        su -c "$*"
+    else
+        echo "Installing to $PREFIX needs root, but there is neither sudo nor su." >&2
+        echo "Use --prefix with a directory you can write to." >&2
+        return 1
+    fi
+}
+
 # The rocksdb and the compression libraries of homebrew are below
 # /opt/homebrew on Apple Silicon, which is not searched by default
 BREW_PREFIX=
@@ -81,14 +106,14 @@
 if [ $SKIP_DEPS = 0 ]; then
     step "Installing the packages of the distribution"
     if command -v dnf > /dev/null; then
-        $SUDO dnf install -y cmake gcc-c++ git make pkgconf-pkg-config \
+        root dnf install -y cmake gcc-c++ git make pkgconf-pkg-config \
              snappy-devel zlib-devel bzip2-devel lz4-devel libzstd-devel
         # Rocky Linux and RHEL have no rocksdb-devel everywhere, that is what
         # step 2 is for
-        $SUDO dnf install -y rocksdb-devel || true
+        root dnf install -y rocksdb-devel || true
     elif command -v apt-get > /dev/null; then
-        $SUDO apt-get update
-        $SUDO apt-get install -y cmake g++ git make pkg-config \
+        root apt-get update
+        root apt-get install -y cmake g++ git make pkg-config \
              librocksdb-dev libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
     elif command -v brew > /dev/null; then
         brew install cmake rocksdb snappy zlib bzip2 lz4 zstd
@@ -141,10 +166,10 @@
 # Programs that link the shared rocksdb of the prefix have to find it. Only a
 # system-wide install can register the directory; for one below $HOME the
 # programs carry the rpath of the libraries they were linked against.
-if [ $ROCKSDB_SHARED_BUILT = 1 ] && [ "$(uname)" = Linux ] && [ -n "$SUDO" ]; then
+if [ $BUILD_ONLY = 0 ] && [ $ROCKSDB_SHARED_BUILT = 1 ] && [ "$(uname)" = Linux ] && [ -n "$NEED_ROOT" ]; then
     RDB_LIBDIR=$(dirname "$(find "$ROCKSDB_PREFIX_USED" -name 'librocksdb.so' | head -1)")
-    echo "$RDB_LIBDIR" | $SUDO tee /etc/ld.so.conf.d/rocksdb-prefix.conf > /dev/null
-    $SUDO ldconfig
+    echo "$RDB_LIBDIR" | root tee /etc/ld.so.conf.d/rocksdb-prefix.conf > /dev/null
+    root ldconfig
 fi
 
 # --- 3. collocatordb ---------------------------------------------------------
@@ -160,9 +185,17 @@
       -DCMAKE_INSTALL_PREFIX="$PREFIX" \
       ${CDB_PREFIXES:+-DCMAKE_PREFIX_PATH=$CDB_PREFIXES}
 cmake --build "$WORK/collocatordb/build" -j "$JOBS"
-$SUDO cmake --install "$WORK/collocatordb/build"
-if [ "$(uname)" = Linux ] && [ -n "$SUDO" ]; then
-    $SUDO ldconfig
+CDB_STAGE=
+if [ $BUILD_ONLY = 1 ]; then
+    # install into a staging prefix only, where the dereko2vec build below
+    # finds the static library and the header
+    CDB_STAGE=$WORK/stage
+    cmake --install "$WORK/collocatordb/build" --prefix "$CDB_STAGE"
+else
+    root cmake --install "$WORK/collocatordb/build"
+    if [ "$(uname)" = Linux ] && [ -n "$NEED_ROOT" ]; then
+        root ldconfig
+    fi
 fi
 if [ $SKIP_TESTS = 0 ]; then
     ctest --test-dir "$WORK/collocatordb/build" --output-on-failure
@@ -175,6 +208,9 @@
 # later install without sudo. Start over, the build takes a minute.
 rm -rf "$DEREKO2VEC_SRC/build"
 CMAKE_PREFIXES=$PREFIX
+if [ -n "$CDB_STAGE" ]; then
+    CMAKE_PREFIXES="$CDB_STAGE;$CMAKE_PREFIXES"
+fi
 if [ -n "$ROCKSDB_PREFIX_USED" ]; then
     CMAKE_PREFIXES="$CMAKE_PREFIXES;$ROCKSDB_PREFIX_USED"
 fi
@@ -195,7 +231,20 @@
 if [ $SKIP_TESTS = 0 ]; then
     ctest --test-dir "$DEREKO2VEC_SRC/build" --output-on-failure
 fi
-$SUDO cmake --install "$DEREKO2VEC_SRC/build"
+if [ $BUILD_ONLY = 0 ]; then
+    root cmake --install "$DEREKO2VEC_SRC/build"
+fi
 
 step "Done"
-echo "dereko2vec and vecs2mmap are in $PREFIX/bin"
+if [ $BUILD_ONLY = 1 ]; then
+    echo "dereko2vec and vecs2mmap are in $DEREKO2VEC_SRC/build"
+    echo "Nothing was installed. Install them with:"
+    echo "  sudo cmake --install \"$DEREKO2VEC_SRC/build\""
+    if [ $STATIC = 0 ] && [ $ROCKSDB_SHARED_BUILT = 1 ]; then
+        RDB_LIBDIR=$(dirname "$(find "$ROCKSDB_PREFIX_USED" -name 'librocksdb.so' | head -1)")
+        echo "With --shared the programs need the rocksdb library at runtime, e.g."
+        echo "  LD_LIBRARY_PATH=$RDB_LIBDIR $DEREKO2VEC_SRC/build/dereko2vec"
+    fi
+else
+    echo "dereko2vec and vecs2mmap are in $PREFIX/bin"
+fi