Commit 304da027 authored by jan.koester's avatar jan.koester
Browse files

bsd build script

parent a76922d7
Loading
Loading
Loading
Loading
+253 −0
Changes for packaging/build-freebsd.sh: 253 added lines, 0 removed lines.
Original line number Diff line number Diff line
#!/bin/sh
# FreeBSD build script for blogi.
#
# Installs third-party dependencies via pkg(8), clones and builds the
# project's own C++ libraries from git.tuxist.de, builds blogi itself and
# packages it as a real pkg(8) .pkg archive (rc.d start script included).
#
# The custom libraries are built and installed straight into $PREFIX (not
# part of the resulting package) — only blogi is packaged. Re-run with
# --skip-deps once they are in place to iterate on blogi alone.
#
# Usage (as root on FreeBSD):
#   ./packaging/build-freebsd.sh [--skip-deps] [--with-editor] [--no-pkg]
#
# Override any git source with a local checkout, e.g.:
#   LIBNETPLUS_SRC=/path/to/libnetplus ./packaging/build-freebsd.sh
set -eu

# ─── configuration ────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"

PREFIX="${PREFIX:-/usr/local}"
JOBS="${JOBS:-$(sysctl -n hw.ncpu)}"
VERSION="${VERSION:-$(date +%Y%m%d)}"
PKGREL="${PKGREL:-0}"

BUILD_ROOT="${BUILD_ROOT:-/var/tmp/blogi-freebsd-build}"
CLONE_DIR="$BUILD_ROOT/sources"
WORK_DIR="$BUILD_ROOT/work"
STAGE_DIR="$BUILD_ROOT/stage"
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_DIR/packaging/output}"

# libdbpp's backends/CMakeLists.txt does find_package(PostgreSQL REQUIRED)
# unconditionally (it also builds a sqlite backend, used by blogi's default
# config, but the postgres client headers/libs must be present regardless).
# The FreeBSD port name is version-suffixed and moves on; override if needed.
PG_PKG="${PG_PKG:-postgresql16-client}"

GIT_BASE="https://git.tuxist.de/git"

# name url branch
DEP_LIBS="
libcmdplus $GIT_BASE/jan.koester/libcmdplus.git master
uuidplus https://tuxist.de/git/tuxist/uuidplus.git main
libnetplus $GIT_BASE/jan.koester/libnetplus.git main
libhtmlpp $GIT_BASE/jan.koester/libhtmlpp.git main
libhttppp $GIT_BASE/jan.koester/libhttppp.git master
libconfplus $GIT_BASE/jan.koester/libconfplus.git main
libdbpp $GIT_BASE/jan.koester/libdbpp.git main
libparitypp $GIT_BASE/tuxist/libparitypp.git main
authdb $GIT_BASE/jan.koester/authdb.git main
smtpclient https://github.com/Tuxist/CPP-SMTPClient-library.git master
"

# Extra per-library cmake args, keyed by name (matches packaging/build-nspawn.sh)
dep_extra_args() {
    case "$1" in
        authdb) echo "-DBUILD_BINDINGS=OFF" ;;
        smtpclient) echo "-DCMAKE_INSTALL_LIBDIR=$PREFIX/lib -DCMAKE_INSTALL_INCLUDEDIR=$PREFIX/include" ;;
        *) echo "" ;;
    esac
}

SKIP_DEPS=0
WITH_EDITOR=0
MAKE_PKG=1
for arg in "$@"; do
    case "$arg" in
        --skip-deps) SKIP_DEPS=1 ;;
        --with-editor) WITH_EDITOR=1 ;;
        --no-pkg) MAKE_PKG=0 ;;
        *) echo "Unknown option: $arg" >&2; exit 1 ;;
    esac
done

# ─── helpers ───────────────────────────────────────────────────────────────
die()  { printf '\033[1;31mERROR:\033[0m %s\n' "$*" >&2; exit 1; }
info() { printf '\033[1;34m==>\033[0m %s\n' "$*" >&2; }
step() { printf '\033[0;36m -->\033[0m %s\n' "$*" >&2; }

# ─── pre-checks ────────────────────────────────────────────────────────────
[ "$(uname -s)" = "FreeBSD" ] || die "This script must run on FreeBSD"
[ "$(id -u)" -eq 0 ] || die "Run as root (pkg install / pw useradd / pkg create need it)"

mkdir -p "$CLONE_DIR" "$WORK_DIR" "$STAGE_DIR" "$OUTPUT_DIR"
rm -rf "$STAGE_DIR"
mkdir -p "$STAGE_DIR"

# ─── system dependencies (pkg) ─────────────────────────────────────────────
info "Installing build tools and third-party libraries via pkg"
pkg install -y \
    cmake ninja pkgconf git \
    json-c brotli tinyxml2 libyaml sqlite3 "$PG_PKG" \
    || die "pkg install failed"

# ─── date (Howard Hinnant) — required on FreeBSD/Apple by blogi's CMakeLists ─
build_date() {
    local name=date url=https://github.com/HowardHinnant/date.git tag=master
    local src="$CLONE_DIR/$name"

    if pkg-config --exists date 2>/dev/null || [ -f "$PREFIX/lib/cmake/date/dateConfig.cmake" ]; then
        step "date already installed, skipping"
        return
    fi

    info "=== date ==="
    if [ -d "$src/.git" ]; then
        git -C "$src" fetch --depth=1 origin "$tag"
        git -C "$src" checkout FETCH_HEAD
    else
        git clone --depth=1 --branch "$tag" "$url" "$src"
    fi

    rm -rf "$WORK_DIR/$name" && mkdir -p "$WORK_DIR/$name"
    cmake -S "$src" -B "$WORK_DIR/$name" -G Ninja \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX="$PREFIX" \
        -DBUILD_SHARED_LIBS=OFF \
        -DUSE_SYSTEM_TZ_DB=ON \
        -DENABLE_DATE_TESTING=OFF
    cmake --build "$WORK_DIR/$name" -j"$JOBS"
    cmake --install "$WORK_DIR/$name"
}

# ─── build_dep <name> <url> <branch> — clone + cmake build + install to $PREFIX
build_dep() {
    local name="$1" url="$2" branch="$3"
    local override_var
    override_var="$(echo "$name" | tr '[:lower:]' '[:upper:]')_SRC"
    eval "local override=\${$override_var:-}"
    local src="$CLONE_DIR/$name"

    info "=== $name ==="
    if [ -n "$override" ] && [ -d "$override" ]; then
        step "using local source override: $override"
        src="$override"
    elif [ -d "$src/.git" ]; then
        step "updating $name"
        git -C "$src" fetch --depth=1 origin "$branch"
        git -C "$src" checkout FETCH_HEAD
    else
        step "cloning $name ($branch)"
        git clone --depth=1 --branch "$branch" "$url" "$src"
    fi

    rm -rf "$WORK_DIR/$name" && mkdir -p "$WORK_DIR/$name"
    # shellcheck disable=SC2046
    cmake -S "$src" -B "$WORK_DIR/$name" -G Ninja \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX="$PREFIX" \
        -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
        $(dep_extra_args "$name")
    cmake --build "$WORK_DIR/$name" -j"$JOBS"
    cmake --install "$WORK_DIR/$name"
}

if [ "$SKIP_DEPS" -eq 0 ]; then
    build_date
    echo "$DEP_LIBS" | while IFS=' ' read -r name url branch; do
        [ -z "$name" ] && continue
        build_dep "$name" "$url" "$branch"
    done
else
    info "Skipping dependency libraries (--skip-deps)"
fi

# ─── blogi itself ──────────────────────────────────────────────────────────
info "=== blogi ==="
BUILD_DIR="$WORK_DIR/blogi"
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR"

BUILD_EDITOR_FLAG=OFF
[ "$WITH_EDITOR" -eq 1 ] && BUILD_EDITOR_FLAG=ON

cmake -S "$PROJECT_DIR" -B "$BUILD_DIR" -G Ninja \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX="$PREFIX" \
    -DBUILD_EDITOR="$BUILD_EDITOR_FLAG"
cmake --build "$BUILD_DIR" -j"$JOBS"

if [ "$MAKE_PKG" -eq 0 ]; then
    info "Installing blogi directly to $PREFIX (--no-pkg)"
    cmake --install "$BUILD_DIR"
    info "Done. Install the rc.d script and default config manually:"
    echo "  install -m 555 $SCRIPT_DIR/freebsd/blogi.rc $PREFIX/etc/rc.d/blogi"
    echo "  install -d -m 755 $PREFIX/etc/blogi"
    echo "  install -m 640 $SCRIPT_DIR/freebsd/blogi.default-config.yaml $PREFIX/etc/blogi/config.yaml"
    exit 0
fi

# ─── stage install ──────────────────────────────────────────────────────────
info "Staging install into $STAGE_DIR"
DESTDIR="$STAGE_DIR" cmake --install "$BUILD_DIR"

install -d "$STAGE_DIR$PREFIX/etc/rc.d"
install -m 555 "$SCRIPT_DIR/freebsd/blogi.rc" "$STAGE_DIR$PREFIX/etc/rc.d/blogi"

install -d "$STAGE_DIR$PREFIX/share/blogi"
install -m 444 "$SCRIPT_DIR/freebsd/blogi.default-config.yaml" \
    "$STAGE_DIR$PREFIX/share/blogi/blogi.default-config.yaml"

# ─── pkg(8) manifest ────────────────────────────────────────────────────────
info "Writing +MANIFEST"
MANIFEST_DIR="$WORK_DIR/manifest"
mkdir -p "$MANIFEST_DIR"

ABI="$(pkg config ABI 2>/dev/null || true)"
[ -z "$ABI" ] && ABI="FreeBSD:$(uname -r | cut -d. -f1):$(uname -m)"

cat > "$MANIFEST_DIR/+MANIFEST" <<EOF
name: blogi
version: "$VERSION.$PKGREL"
origin: www/blogi
comment: "Blog engine with web-based editor"
desc: "blogi is a blog engine with an integrated web-based editor for editing and publishing blog content."
maintainer: jan.koester@tuxist.de
www: https://git.tuxist.de/jan.koester/blogi
abi: "$ABI"
prefix: "$PREFIX"
licenselogic: single
categories: [www]
scripts: {
  post-install: <<EOD
if ! pw groupshow blogi >/dev/null 2>&1; then
    pw groupadd blogi
fi
if ! pw usershow blogi >/dev/null 2>&1; then
    pw useradd blogi -g blogi -d /var/lib/blogi -s /usr/sbin/nologin -c "Blogi daemon" -w no
fi
install -d -o blogi -g blogi -m 0750 /var/lib/blogi
install -d -o blogi -g blogi -m 0750 /var/log/blogi
install -d -m 0755 $PREFIX/etc/blogi
if [ ! -f $PREFIX/etc/blogi/config.yaml ]; then
    install -o root -g blogi -m 0640 $PREFIX/share/blogi/blogi.default-config.yaml $PREFIX/etc/blogi/config.yaml
fi
echo "===> blogi installed."
echo "===> Edit $PREFIX/etc/blogi/config.yaml, then:"
echo "       sysrc blogi_enable=YES"
echo "       service blogi start"
EOD
}
EOF

# blogi links against libraries built from source above (not registered pkg
# origins), so they cannot be declared as pkg 'deps' here — see comment at
# the top of this script. Run 'ldd $PREFIX/bin/blogi' to inspect at runtime.

info "Building pkg(8) package"
pkg create -m "$MANIFEST_DIR" -r "$STAGE_DIR" -o "$OUTPUT_DIR"

info "Done — package in $OUTPUT_DIR"
ls "$OUTPUT_DIR"/blogi-*