Created
September 27, 2025 00:20
-
-
Save abiiranathan/036b62fb3a342afbeed79b84798db26f to your computer and use it in GitHub Desktop.
Script to Statically Compile Poppler-Glib
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash -eux | |
| # This script builds a static version of poppler and its dependencies. | |
| # The script is based on the instructions and examples provided in the article: | |
| # https://fasterthanli.me/series/dont-shell-out/part-3 | |
| # Get the absolute path of the script. | |
| SCRIPT_PATH=$(readlink -f "$0") | |
| # Get the directory where the script is located. | |
| SCRIPT_DIR=$(dirname "$SCRIPT_PATH") | |
| # Set the installation directory for the static libraries and headers. | |
| export PREFIX="${SCRIPT_DIR}/prefix" | |
| mkdir -p "${PREFIX}" | |
| # Set the PKG_CONFIG_PATH to use the libraries we build. | |
| export PKG_CONFIG_PATH="${PREFIX}/lib/pkgconfig:${PREFIX}/lib64/pkgconfig" | |
| # Set compiler flags for building position-independent code (PIC). | |
| export CFLAGS="-fPIC" | |
| export CXXFLAGS="-fPIC" | |
| # Function to check if a library is already installed in our PREFIX | |
| is_installed() { | |
| local pkgname=$1 | |
| local libfile=$2 # Optional: specific library file to check | |
| # Only check for the library file in our PREFIX directory | |
| # Don't use pkg-config as it might find system libraries | |
| if [[ -n "${libfile:-}" && -f "$PREFIX/lib/$libfile" ]]; then | |
| echo ">>> $pkgname already installed (found $libfile in $PREFIX). Skipping." | |
| return 0 | |
| fi | |
| # Fallback: check if pkg-config finds it specifically in our PREFIX | |
| local pc_file="$PREFIX/lib/pkgconfig/$pkgname.pc" | |
| if [[ -f "$pc_file" ]]; then | |
| echo ">>> $pkgname already installed (found $pc_file). Skipping." | |
| return 0 | |
| fi | |
| return 1 | |
| } | |
| # Function to download and extract a tarball. | |
| download_and_extract() { | |
| local url=$1 | |
| local dir=$2 | |
| local strip_components=${3:-1} | |
| local tar_flags | |
| # Check if sources already exist | |
| if [[ -d "sources/${dir}" ]]; then | |
| echo ">>> Sources for ${dir} already exist. Skipping download." | |
| return | |
| fi | |
| # Determine the tar flags based on file extension | |
| case "$url" in | |
| *.tar.xz) | |
| tar_flags="-xJ" | |
| ;; | |
| *.tar.gz) | |
| tar_flags="-xz" | |
| ;; | |
| *) | |
| echo "Unsupported archive format for URL: $url" | |
| exit 1 | |
| ;; | |
| esac | |
| echo ">>> Downloading and extracting $dir" | |
| mkdir -p "sources/${dir}" | |
| curl -f -L "${url}" | tar "${tar_flags}" --strip-components="${strip_components}" -C "sources/${dir}" | |
| } | |
| # Function to build with configure (autotools) | |
| build_autotools() { | |
| local name=$1 | |
| local source_dir=$2 | |
| local build_dir=$3 | |
| shift 3 | |
| # remaining args are passed to configure | |
| echo ">>> Building $name with autotools" | |
| rm -rf "builds/$build_dir" | |
| mkdir -p "builds/$build_dir" | |
| pushd "builds/$build_dir" | |
| "../../sources/$source_dir/configure" --prefix="${PREFIX}" --disable-shared --enable-static "$@" | |
| make -j "$(nproc)" | |
| make install | |
| popd | |
| } | |
| # Function to build with cmake | |
| build_cmake() { | |
| local name=$1 | |
| local source_dir=$2 | |
| local build_dir=$3 | |
| shift 3 | |
| # remaining args are passed to cmake | |
| echo ">>> Building $name with cmake" | |
| rm -rf "builds/$build_dir" | |
| mkdir -p "builds/$build_dir" | |
| pushd "builds/$build_dir" | |
| cmake -S "../../sources/$source_dir" -B . \ | |
| -DCMAKE_INSTALL_PREFIX="${PREFIX}" \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| "$@" | |
| cmake --build . --parallel "$(nproc)" | |
| cmake --install . | |
| popd | |
| } | |
| # Function to build with meson | |
| build_meson() { | |
| local name=$1 | |
| local source_dir=$2 | |
| local build_dir=$3 | |
| shift 3 | |
| # remaining args are passed to meson | |
| echo ">>> Building $name with meson" | |
| rm -rf "builds/$build_dir" | |
| mkdir -p "builds/$build_dir" | |
| pushd "builds/$build_dir" | |
| meson setup . "../../sources/$source_dir" \ | |
| --prefix="${PREFIX}" \ | |
| --buildtype=release \ | |
| --default-library=static \ | |
| --prefer-static \ | |
| "$@" | |
| ninja -v | |
| ninja install | |
| popd | |
| } | |
| # --- Dependencies --- | |
| # zlib | |
| if ! is_installed "zlib" "libz.a"; then | |
| download_and_extract "https://www.zlib.net/zlib-1.3.1.tar.xz" "zlib" | |
| echo ">>> Building zlib" | |
| rm -rf builds/zlib | |
| mkdir -p builds/zlib | |
| pushd builds/zlib | |
| ../../sources/zlib/configure --prefix="${PREFIX}" --static | |
| make -j "$(nproc)" | |
| make install | |
| popd | |
| fi | |
| # libpng | |
| if ! is_installed "libpng" "libpng.a"; then | |
| download_and_extract "https://download.sourceforge.net/libpng/libpng-1.6.40.tar.xz" "libpng" | |
| build_autotools "libpng" "libpng" "libpng" | |
| fi | |
| # freetype | |
| if ! is_installed "freetype2" "libfreetype.a"; then | |
| download_and_extract "https://download.savannah.gnu.org/releases/freetype/freetype-2.13.2.tar.xz" "freetype" | |
| build_autotools "freetype" "freetype" "freetype" \ | |
| --with-png=no --with-zlib=yes --with-harfbuzz=no --with-bzip2=no --with-brotli=no | |
| fi | |
| # fontconfig | |
| if ! is_installed "fontconfig" "libfontconfig.a"; then | |
| download_and_extract "https://www.freedesktop.org/software/fontconfig/release/fontconfig-2.15.0.tar.xz" "fontconfig" | |
| build_autotools "fontconfig" "fontconfig" "fontconfig" --disable-docs | |
| fi | |
| # pixman | |
| if ! is_installed "pixman-1" "libpixman-1.a"; then | |
| download_and_extract "https://www.cairographics.org/releases/pixman-0.43.4.tar.gz" "pixman" | |
| build_meson "pixman" "pixman" "pixman" -Dtests=disabled -Dgtk=disabled | |
| fi | |
| # First, clean up the existing cairo build | |
| echo ">>> Cleaning existing cairo build" | |
| # rm -rf builds/cairo | |
| # rm -f "${PREFIX}/lib/libcairo.a" | |
| # rm -f "${PREFIX}/lib/pkgconfig/cairo.pc" | |
| # rm -rf "${PREFIX}/include/cairo" | |
| # exit 0 | |
| if ! is_installed "cairo" "libcairo.a"; then | |
| download_and_extract "https://www.cairographics.org/releases/cairo-1.18.4.tar.xz" "cairo" | |
| echo ">>> Building cairo with static pixman linking" | |
| rm -rf "builds/cairo" | |
| mkdir -p "builds/cairo" | |
| pushd "builds/cairo" | |
| # Set environment variables for static linking | |
| export PIXMAN_CFLAGS="$(pkg-config --cflags pixman-1)" | |
| export PIXMAN_LIBS="$(pkg-config --libs --static pixman-1)" | |
| export LDFLAGS="-L${PREFIX}/lib" | |
| export LIBS="-lpixman-1" | |
| meson setup . "../../sources/cairo" \ | |
| --prefix="$PREFIX" \ | |
| --buildtype=release \ | |
| --default-library=static \ | |
| --prefer-static \ | |
| -Dglib=disabled \ | |
| -Dfontconfig=enabled \ | |
| -Dfreetype=enabled \ | |
| -Dwerror=false \ | |
| -Dtests=disabled | |
| ninja -v | |
| ninja install | |
| # Cleanup environment | |
| unset PIXMAN_CFLAGS PIXMAN_LIBS LDFLAGS LIBS | |
| popd | |
| fi | |
| # openjpeg | |
| if ! is_installed "libopenjp2" "libopenjp2.a"; then | |
| download_and_extract "https://github.com/uclouvain/openjpeg/archive/v2.5.4.tar.gz" "openjpeg" | |
| build_cmake "openjpeg" "openjpeg" "openjpeg" | |
| fi | |
| # poppler-data | |
| if [[ ! -d "$PREFIX/share/poppler" ]]; then | |
| download_and_extract "https://poppler.freedesktop.org/poppler-data-0.4.12.tar.gz" "poppler-data" | |
| echo ">>> Installing poppler-data (data files only)" | |
| pushd sources/poppler-data/poppler-data-0.4.12 | |
| # First run make to generate poppler-data.pc | |
| make | |
| # Then install with our custom prefix | |
| make install datadir="${PREFIX}/share" | |
| popd | |
| else | |
| echo ">>> poppler-data already installed. Skipping." | |
| fi | |
| # # glib (required for poppler-glib) | |
| # Add before poppler build - minimal gobject-2.0 for core C API | |
| if ! is_installed "gobject-2.0" "libgobject-2.0.a"; then | |
| # We need minimal glib just for GObject, not the full glib ecosystem | |
| download_and_extract "https://download.gnome.org/sources/glib/2.78/glib-2.78.1.tar.xz" "glib" | |
| build_meson "glib (minimal)" "glib" "glib" \ | |
| -Dtests=false \ | |
| -Dgtk_doc=false \ | |
| -Dman=false \ | |
| -Dnls=disabled \ | |
| -Dlibmount=disabled \ | |
| -Dselinux=disabled | |
| fi | |
| # --- Poppler --- | |
| # Build poppler with core C API enabled | |
| if ! is_installed "poppler" "libpoppler.a"; then | |
| download_and_extract "https://poppler.freedesktop.org/poppler-25.09.1.tar.xz" "poppler" | |
| build_cmake "poppler" "poppler" "poppler" \ | |
| -DCMAKE_INSTALL_PREFIX=${PREFIX} \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_POSITION_INDEPENDENT_CODE=True \ | |
| -DBUILD_GTK_TESTS=OFF \ | |
| -DBUILD_QT5_TESTS=OFF \ | |
| -DBUILD_QT6_TESTS=OFF \ | |
| -DBUILD_CPP_TESTS=OFF \ | |
| -DBUILD_MANUAL_TESTS=OFF \ | |
| -DENABLE_BOOST=OFF \ | |
| -DENABLE_UTILS=OFF \ | |
| -DENABLE_CPP=OFF \ | |
| -DENABLE_GLIB=ON \ | |
| -DENABLE_GOBJECT_INTROSPECTION=OFF \ | |
| -DENABLE_GTK_DOC=OFF \ | |
| -DENABLE_QT5=OFF \ | |
| -DENABLE_QT6=OFF \ | |
| -DENABLE_LIBOPENJPEG=none \ | |
| -DENABLE_CMS=none \ | |
| -DENABLE_DCTDECODER=none \ | |
| -DENABLE_LIBCURL=OFF \ | |
| -DENABLE_ZLIB=OFF \ | |
| -DBUILD_SHARED_LIBS=OFF \ | |
| -DRUN_GPERF_IF_PRESENT=OFF | |
| fi | |
| echo "Poppler static build complete." | |
| echo "Libraries and headers are in the '${PREFIX}' directory." | |
| echo | |
| echo "To use in your projects:" | |
| echo " export PKG_CONFIG_PATH=\"${PREFIX}/lib/pkgconfig:${PREFIX}/lib64/pkgconfig\"" | |
| echo " pkg-config --cflags --libs poppler" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment