Skip to content

Support for arm64 #1

@moll3r

Description

@moll3r

Hi there,

In an effort to get this project running on my Raspberry Pi 5, I discovered that there are no pre-compiled watchman releases for arm64.

To solve this, I was able to successfully compile from source. This is a very large compile, and I was not able to complete it on my Raspberry Pi 5. A Hetzner VPS with 16 Cores and 32GB luckily did the trick.

Hopefully this helps add support for arm64 in the main install script.

Below is the script I came up with to successfully compile on a fresh install of Ubuntu 22.04. It automatically takes care of everything, and spits out an installable .deb file.

For your convenience, A working pre-compiled package is here: https://tixo.net/proton-drive-sync/watchman_2025.12.29.00_arm64.deb

If you wish to compile yourself:
Compilation Script (For Ubuntu 22.04)

$ bash <(curl -fsSL https://tixo.net/proton-drive-sync/watchman_arm64.sh)
#!/bin/bash

# ==============================================================================
# Watchman Builder & Packager: Standalone Distribution
# ==============================================================================
# This script builds a portable Debian package for Facebook Watchman.
#
# Workflow:
# 1. Downloads the specific release tarball from GitHub.
# 2. Cleans the build environment to ensure dependency compatibility.
# 3. Compiles from source using standard build tools.
# 4. Identifies and bundles necessary shared libraries (Boost, Glog, etc.).
# 5. Patches binaries for RPATH isolation to prevent system conflicts.
# ==============================================================================

set -e

# ==============================================================================
# CONFIGURATION
# ==============================================================================
WATCHMAN_TAG="v2025.12.29.00"
RUST_CHANNEL="stable"
REQUIRED_SPACE_KB=14680064 # ~14GB

# ==============================================================================
# 0. Output Helpers
# ==============================================================================
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
NC='\033[0m'

log() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }

# ==============================================================================
# 1. Pre-flight Checks & Cleanup
# ==============================================================================
log "Checking system resources..."
AVAILABLE_SPACE=$(df -k /tmp | awk 'NR==2 {print $4}')

if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_SPACE_KB" ]; then
    error "Insufficient disk space in /tmp. Required: 14GB."
    exit 1
fi

# CRITICAL: Clean build cache to ensure fresh dependency resolution
log "Cleaning stale dependency caches in /tmp..."
sudo rm -rf /tmp/fbcode_builder_getdeps*
success "Dependency cache cleared."

# ==============================================================================
# 2. System Dependencies
# ==============================================================================
log "Installing build dependencies..."
sudo apt-get update
# patchelf is required for the RPATH isolation step
sudo apt-get install -y \
    libbz2-dev liblzma-dev libzstd-dev libboost-all-dev \
    libgoogle-glog-dev libgflags-dev libevent-dev \
    libdouble-conversion-dev libssl-dev build-essential \
    curl tar git libunwind-dev pipx patchelf

# ==============================================================================
# 3. Rust Environment Setup
# ==============================================================================
if [ -f "$HOME/.cargo/env" ]; then source "$HOME/.cargo/env"; fi

if command -v rustc &> /dev/null; then
    log "Rust is already installed: $(rustc --version)"
else
    log "Installing Rust..."
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain "$RUST_CHANNEL" --profile complete
    source "$HOME/.cargo/env"
fi

# ==============================================================================
# 4. Python Tooling (Pex)
# ==============================================================================
export PATH="$PATH:$HOME/.local/bin"
if ! command -v pex &> /dev/null; then
    log "Installing pex via pipx..."
    pipx install pex
    pipx ensurepath
else
    log "Pex is already installed."
fi

# ==============================================================================
# 5. Source Code Retrieval
# ==============================================================================
WATCHMAN_DIR="watchman_build"
TARBALL_URL="https://github.com/facebook/watchman/archive/refs/tags/${WATCHMAN_TAG}.tar.gz"

if [ -d "$WATCHMAN_DIR" ]; then
    log "Cleaning up old build directory..."
    rm -rf "$WATCHMAN_DIR"
fi

mkdir -p "$WATCHMAN_DIR"
log "Downloading source release: $WATCHMAN_TAG"
log "URL: $TARBALL_URL"

# Download and extract, stripping the top-level directory (usually watchman-TAG)
curl -L "$TARBALL_URL" | tar -xz -C "$WATCHMAN_DIR" --strip-components=1

cd "$WATCHMAN_DIR"

# === Patch CMakeLists.txt ===
# We directly edit the build definition to force our version.
# We replace: set(WATCHMAN_VERSION_OVERRIDE "" ...
# With:       set(WATCHMAN_VERSION_OVERRIDE "2025.12.29.00" ...
VERSION_NUM="${WATCHMAN_TAG#v}"
log "Patching CMakeLists.txt to force version: $VERSION_NUM"

sed -i "s/set(WATCHMAN_VERSION_OVERRIDE \"\"/set(WATCHMAN_VERSION_OVERRIDE \"$VERSION_NUM\"/" CMakeLists.txt

# Also patch the fallback just in case
sed -i "s/set(PACKAGE_VERSION \"0.0.0\")/set(PACKAGE_VERSION \"$VERSION_NUM\")/" CMakeLists.txt
# === FIX END ===

# ==============================================================================
# 6. Patching Build Scripts
# ==============================================================================
log "Patching build/fbcode_builder/getdeps.py..."
# Remove internal pip install command to avoid conflicts with system python
sed -i '/cmd_argss.append(\["pip", "install", "pex"\])/d' build/fbcode_builder/getdeps.py

# ==============================================================================
# 7. Build Execution
# ==============================================================================
log "Running install-system-packages.sh..."
./install-system-packages.sh

log "Running autogen.sh (This will take a while)..."
./autogen.sh

# ==============================================================================
# 8. Standalone Packaging Process
# ==============================================================================
log "Starting Standalone Packaging Process..."

# Set version
VERSION="${WATCHMAN_TAG#v}"
VERSION="${VERSION:-$(date +%Y.%m.%d.00)}"

PKG_NAME="watchman"
ARCH=$(dpkg --print-architecture)
DEB_FILENAME="${PKG_NAME}_${VERSION}_${ARCH}.deb"
WORK_DIR="deb_package"

# 1. Prepare Workspace
rm -rf "$WORK_DIR"
mkdir -p "$WORK_DIR/DEBIAN"
mkdir -p "$WORK_DIR/usr/local/bin"
mkdir -p "$WORK_DIR/usr/local/lib/watchman" 
mkdir -p "$WORK_DIR/usr/local/var/run/watchman"

# 2. Copy Binaries
log "Copying compiled binaries..."
if [ -d "built/bin" ]; then
    cp -r built/bin/* "$WORK_DIR/usr/local/bin/"
else
    error "Build failed: built/bin missing."
    exit 1
fi

# 3. Dependency Identification & Bundling
log "Analyzing and bundling non-system dependencies..."

# Filter function: Returns 0 (True) if we should bundle, 1 (False) if Core OS lib
should_bundle() {
    local libname="$1"
    # Exclude core OS libs: libc, libssl (openssl), libstdc++, libgcc, dynamic linker
    if [[ "$libname" =~ libc\.so || "$libname" =~ libstdc\+\+ || "$libname" =~ libgcc_s || \
          "$libname" =~ libm\.so || "$libname" =~ libpthread || "$libname" =~ libdl || \
          "$libname" =~ ld-linux || "$libname" =~ libssl || "$libname" =~ libcrypto ]]; then
        return 1 # False (Do not bundle)
    fi
    return 0 # True (Bundle)
}

# Identify dependencies via ldd
DEPENDENCIES=$(ldd built/bin/watchman | awk '{print $3}' | grep "^/")

# Include libraries built by Watchman itself (e.g. libfolly, libfizz)
for f in built/lib/*.so*; do
    if [ -f "$f" ]; then cp "$f" "$WORK_DIR/usr/local/lib/watchman/"; fi
done

# Include necessary system libraries (e.g. libboost, libglog)
for dep_path in $DEPENDENCIES; do
    lib_name=$(basename "$dep_path")
    if should_bundle "$lib_name"; then
        echo "   [BUNDLE] $lib_name"
        cp "$dep_path" "$WORK_DIR/usr/local/lib/watchman/"
    else
        echo "   [SYSTEM] $lib_name (Skipping)"
    fi
done

# 4. Portability Sanitization (RPATH & Absolute Paths)
log "Applying RPATH isolation and sanitizing absolute paths..."

patch_file() {
    local file="$1"
    local is_bin="$2"

    # A. Strip absolute paths (ensure linkage uses relative names)
    patchelf --print-needed "$file" | grep "/" | while read -r bad_dep; do
        clean_dep=$(basename "$bad_dep")
        patchelf --replace-needed "$bad_dep" "$clean_dep" "$file"
    done

    # B. Set RPATH to look in the bundled directory
    if [ "$is_bin" -eq 1 ]; then
        patchelf --set-rpath '$ORIGIN/../lib/watchman' "$file"
    else
        patchelf --set-rpath '$ORIGIN' "$file"
    fi
}

# Patch Binaries
patch_file "$WORK_DIR/usr/local/bin/watchman" 1
patch_file "$WORK_DIR/usr/local/bin/watchmanctl" 1

# Patch Libraries
for lib in "$WORK_DIR/usr/local/lib/watchman"/*.so*; do
    patch_file "$lib" 0
done

# 5. Create Control File
# Note: Depends on libssl3 (runtime) instead of libssl-dev (headers)
cat <<EOF > "$WORK_DIR/DEBIAN/control"
Package: $PKG_NAME
Version: $VERSION
Architecture: $ARCH
Maintainer: Admin <admin@localhost>
Depends: libc6, libstdc++6, libgcc-s1, libssl3
Section: utils
Priority: optional
Description: Watchman (Standalone)
 Facebook's Watchman, compiled for Ubuntu 22.04.
 This package is self-contained and includes private versions of
 necessary libraries (Boost, Glog, Folly, etc.) to ensure
 portability and prevent system conflicts.
EOF

# 6. Post-Install Script
cat <<EOF > "$WORK_DIR/DEBIAN/postinst"
#!/bin/sh
set -e
mkdir -p /usr/local/var/run/watchman
chmod 2777 /usr/local/var/run/watchman
exit 0
EOF
chmod 755 "$WORK_DIR/DEBIAN/postinst"

# 7. Build Package
log "Building .deb package..."
dpkg-deb --build "$WORK_DIR" "../$DEB_FILENAME"
rm -rf "$WORK_DIR"

echo ""
echo "----------------------------------------------------"
success "Standalone Package created: $(dirname $(pwd))/$DEB_FILENAME"
echo "----------------------------------------------------"


Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions