#!/usr/bin/env bash set -e SCRIPT_PATH= PREFIX= DOWNLOAD_CACHE=work OPENRESTY_VER= OPENSSL_VER= LUAROCKS_VER= PCRE_VER= FORCE=0 OPENRESTY_PATCHES= OPENRESTY_PATCHES_DIR= KONG_NGINX_MODULE=master DEBUG=0 NPROC= OS= DIST= DIST_VER= NGINX_EXTRA_MODULES=() PARAMS="" main() { OS=$(uname -s) NPROC=$(n_proc) SCRIPT_PATH=$(dirname "$(canon_path $0)") while (( "$#" )); do case "$1" in -p|--prefix) PREFIX=$2 shift 2 ;; -j|--jobs) NPROC=$2 shift 2 ;; --pcre) PCRE_VER=$2 shift 2 ;; --pcre_sha) PCRE_SHA=$2 shift 2 ;; --openresty) OPENRESTY_VER=$2 shift 2 ;; --openresty_sha) OPENRESTY_SHA=$2 shift 2 ;; --openssl) OPENSSL_VER=$2 shift 2 ;; --openssl_sha) OPENSSL_SHA=$2 shift 2 ;; --luarocks) LUAROCKS_VER=$2 shift 2 ;; --luarocks_sha) LUAROCKS_SHA=$2 shift 2 ;; --no-openresty-patches) OPENRESTY_PATCHES=0 shift 1 ;; --openresty-patches) if [ ! -z "$OPENRESTY_PATCHES_DIR" ]; then fatal "Can not specify --openresty-patches and --openresty-patches-dir at the same time" fi OPENRESTY_PATCHES=$2 shift 2 ;; --openresty-patches-dir) if [ ! -z "$OPENRESTY_PATCHES" ]; then fatal "Can not specify --openresty-patches-dir and --openresty-patches at the same time" fi OPENRESTY_PATCHES_DIR=$2 shift 2 ;; --kong-nginx-module) KONG_NGINX_MODULE=$2 shift 2 ;; --no-kong-nginx-module) KONG_NGINX_MODULE=0 shift 1 ;; -f|--force) FORCE=1 shift 1 ;; --debug) DEBUG=1 shift 1 ;; --work) DOWNLOAD_CACHE=$2 shift 2 ;; --add-module) NGINX_EXTRA_MODULES+=("--add-module=$2") shift 2 ;; -h|--help) show_usage exit 0 ;; --) # end argument parsing shift break ;; -*|--*=) # unsupported flags echo "Error: Unsupported flag $1" >&2 exit 1 ;; *) # preserve positional arguments PARAMS="$PARAMS $1" shift ;; esac done # set positional arguments in their proper place eval set -- "$PARAMS" if [ -z "$OPENRESTY_PATCHES" ]; then OPENRESTY_PATCHES=master fi if [ -z "$PREFIX" ]; then show_usage fatal "prefix can not be empty" fi PREFIX=`canon_path $PREFIX` DOWNLOAD_CACHE=`canon_path $DOWNLOAD_CACHE` if [ -z "$OPENRESTY_VER" ]; then show_usage fatal "OpenResty version can not be empty" fi if [ -z "$OPENSSL_VER" ]; then show_usage fatal "OpenSSL version can not be empty" fi # retrieve DIST info of DIST-specific patches if [ -f /etc/os-release ]; then . /etc/os-release DIST=$NAME DIST_VER=$VERSION_ID elif type lsb_release >/dev/null 2>&1; then DIST=$(lsb_release -si) DIST_VER=$(lsb_release -sr) fi if [ $FORCE == 1 ]; then rm -rf $PREFIX $DOWNLOAD_CACHE fi NGINX_CORE_VER=$(parse_nginx_core_version $OPENRESTY_VER) OPENSSL_DOWNLOAD=$DOWNLOAD_CACHE/openssl-$OPENSSL_VER OPENRESTY_DOWNLOAD=$DOWNLOAD_CACHE/openresty-$OPENRESTY_VER mkdir -p $DOWNLOAD_CACHE $PREFIX OPENSSL_DESTDIR=${OPENSSL_DESTDIR:-/} OPENRESTY_DESTDIR=${OPENRESTY_DESTDIR:-/} OPENSSL_PREFIX=${OPENSSL_PREFIX:-$PREFIX/openssl} OPENRESTY_PREFIX=${OPENRESTY_PREFIX:-$PREFIX/openresty} OPENSSL_INSTALL=$(canon_path $OPENSSL_DESTDIR/$OPENSSL_PREFIX) OPENRESTY_INSTALL=$(canon_path $OPENRESTY_DESTDIR/$OPENRESTY_PREFIX) if version_lt $NGINX_CORE_VER 1.13.6 || version_lt $OPENSSL_VER 1.1; then # unconditionally disable module in older core since they are never tested KONG_NGINX_MODULE=0 fi notice "Downloading the components now..." pushd $DOWNLOAD_CACHE # OpenSSL if [[ ! -f $OPENSSL_INSTALL/bin/openssl && ! -d $OPENSSL_DOWNLOAD ]]; then warn "OpenSSL source not found, downloading..." curl -sSLO https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz if [ ! -z ${OPENSSL_SHA+x} ]; then echo "$OPENSSL_SHA openssl-$OPENSSL_VER.tar.gz" | sha256sum -c - fi tar -xzvf openssl-$OPENSSL_VER.tar.gz fi # OpenResty if [ ! -f $OPENRESTY_INSTALL/nginx/sbin/nginx ]; then if [[ $OPENRESTY_PATCHES == 0 && -f $OPENRESTY_DOWNLOAD/bundle/.patch_applied ]]; then warn "Patched OpenResty found but vanilla requested, removing source..." rm -rf $OPENRESTY_DOWNLOAD elif [[ $OPENRESTY_PATCHES != 0 && ! -f $OPENRESTY_DOWNLOAD/bundle/.patch_applied ]]; then warn "Vanilla OpenResty found but patches requested, removing Makefile..." rm -f $OPENRESTY_DOWNLOAD/Makefile fi if [ ! -d $OPENRESTY_DOWNLOAD ]; then warn "OpenResty source not found, downloading..." curl -sSLO https://openresty.org/download/openresty-$OPENRESTY_VER.tar.gz if [ ! -z ${OPENRESTY_SHA+x} ]; then echo "$OPENRESTY_SHA openresty-$OPENRESTY_VER.tar.gz" | sha256sum -c - fi tar -xzvf openresty-$OPENRESTY_VER.tar.gz fi fi # PCRE if [ ! -z "$PCRE_VER" ]; then PCRE_DOWNLOAD=$DOWNLOAD_CACHE/pcre-$PCRE_VER if [ ! -d $PCRE_DOWNLOAD ]; then warn "PCRE source not found, downloading..." curl -sSLO https://ftp.pcre.org/pub/pcre/pcre-${PCRE_VER}.tar.gz if [ ! -z ${PCRE_SHA+x} ]; then echo "$PCRE_SHA pcre-${PCRE_VER}.tar.gz" | sha256sum -c - fi tar -xzvf pcre-${PCRE_VER}.tar.gz fi fi # LuaRocks if [ ! -z "$LUAROCKS_VER" ]; then LUAROCKS_DESTDIR=${LUAROCKS_DESTDIR:-/} LUAROCKS_PREFIX=${LUAROCKS_PREFIX:-$PREFIX/luarocks} LUAROCKS_INSTALL=$(canon_path $LUAROCKS_DESTDIR/$LUAROCKS_PREFIX) if [ ! -f $LUAROCKS_INSTALL/bin/luarocks ]; then LUAROCKS_DOWNLOAD=$DOWNLOAD_CACHE/luarocks-$LUAROCKS_VER if [ ! -d $LUAROCKS_DOWNLOAD ]; then warn "LuaRocks source not found, downloading..." curl -sSLO https://luarocks.org/releases/luarocks-$LUAROCKS_VER.tar.gz if [ ! -z ${LUAROCKS_SHA+x} ]; then echo "$LUAROCKS_SHA luarocks-$LUAROCKS_VER.tar.gz" | sha256sum -c - fi tar -xzvf luarocks-$LUAROCKS_VER.tar.gz fi fi fi # lua-kong-nginx-module if [ $KONG_NGINX_MODULE != 0 ]; then pushd $DOWNLOAD_CACHE if [ ! -d lua-kong-nginx-module ]; then warn "lua-kong-nginx-module source not found, cloning..." git clone https://github.com/Kong/lua-kong-nginx-module fi pushd lua-kong-nginx-module git fetch git reset --hard origin/$KONG_NGINX_MODULE popd popd fi popd notice "Patching the components now..." if [ ! -f $OPENRESTY_INSTALL/nginx/sbin/nginx ]; then notice "Patching OpenResty..." if [[ $OPENRESTY_PATCHES != 0 ]]; then if [[ -z "$OPENRESTY_PATCHES_DIR" ]]; then pushd $DOWNLOAD_CACHE if [[ ! -d "openresty-patches" ]]; then warn "Kong OpenResty patches not found, cloning..." git clone https://github.com/Kong/openresty-patches fi pushd openresty-patches notice "Checking out branch '$OPENRESTY_PATCHES' of Kong's OpenResty patches..." git fetch git reset --hard origin/$OPENRESTY_PATCHES popd popd OPENRESTY_PATCHES_DIR=$DOWNLOAD_CACHE/openresty-patches/ fi if [[ ! -d "$OPENRESTY_PATCHES_DIR" ]]; then fatal "directory does not exist: $OPENRESTY_PATCHES_DIR" fi pushd $OPENRESTY_DOWNLOAD/bundle if [ ! -f .patch_applied ]; then for patch_file in $(ls -1 $OPENRESTY_PATCHES_DIR/patches/$OPENRESTY_VER/*.patch); do notice "Applying OpenResty patch $patch_file" patch -p1 < $patch_file \ || fatal "failed to apply patch: $patch_file" done touch .patch_applied fi popd if [ ! -f $OPENRESTY_DOWNLOAD/bundle/.patch_applied ]; then fatal "missing .patch_applied file; some OpenResty patches may not have been applied" fi fi # apply non Kong-specific patches if version_lt $NGINX_CORE_VER 1.15.0; then # this is fixed in Nginx 1.15.0 if [[ $DIST == "Fedora" && $DIST_VER -gt 28 ]]; then warn "Fedora 28 or above detected, applying the 'rm_glibc_crypt_r_workaround' patch..." pushd $OPENRESTY_DOWNLOAD/bundle/nginx-$NGINX_CORE_VER patch --forward -p1 < $SCRIPT_PATH/patches/nginx-$NGINX_CORE_VER-rm_glibc_crypt_r_workaround.patch || true popd fi fi if version_eq $OPENRESTY_VER 1.15.8.1; then # this appeared in OpenResty 1.15.8.1 and is fixed in later versions if [ ! -z "$PCRE_VER" ]; then warn "Building OpenResty 1.15.8.1 with static libpcre, applying the 'fix_static_libpcre_linking' patch..." pushd $OPENRESTY_DOWNLOAD/bundle/ngx_lua-0.10.15 patch --forward -p1 < $SCRIPT_PATH/patches/openresty-$OPENRESTY_VER-fix_static_libpcre_linking.patch || true popd fi fi fi notice "Building the components now..." # Building OpenSSL if [ ! -f $OPENSSL_INSTALL/bin/openssl ]; then notice "Building OpenSSL..." pushd $OPENSSL_DOWNLOAD if (version_lte $OPENSSL_VER 1.0 && [[ ! -d include/openssl ]]) || [[ ! -f Makefile ]]; then OPENSSL_OPTS=( "-g" "shared" "-DPURIFY" "no-threads" "--prefix=$OPENSSL_PREFIX" "--openssldir=$OPENSSL_PREFIX" ) if version_gte $OPENSSL_VER 1.1.0; then OPENSSL_OPTS+=('no-unit-test') else OPENSSL_OPTS+=('no-tests') fi if ([[ $CC == "clang" ]] && version_gte $OPENSSL_VER 1.1) || [[ $CC != "clang" ]]; then local ld_opts="-Wl,-rpath,'\$(LIBRPATH)'" if [[ $OS != "Darwin" ]]; then ld_opts="$ld_opts,--enable-new-dtags" fi OPENSSL_OPTS+=("$ld_opts") fi if [ $DEBUG == 1 ]; then OPENSSL_OPTS+=('-d') fi eval ./config ${OPENSSL_OPTS[*]} fi if version_gte $OPENSSL_VER 1.1.0; then make -j$NPROC else make fi make install_sw DESTDIR=${OPENSSL_DESTDIR} popd succ "OpenSSL $OPENSSL_VER has been built successfully!" else succ "OpenSSL $OPENSSL_VER has been built successfully (cached)!" fi # Building OpenResty if [ ! -f $OPENRESTY_INSTALL/nginx/sbin/nginx ]; then notice "Building OpenResty..." pushd $OPENRESTY_DOWNLOAD if [ ! -f Makefile ]; then OPENRESTY_RPATH=${OPENRESTY_RPATH:-$OPENSSL_INSTALL/lib} OPENRESTY_OPTS=( "--prefix=$OPENRESTY_PREFIX" "--with-cc-opt='-I$OPENSSL_INSTALL/include'" "--with-ld-opt='-L$OPENSSL_INSTALL/lib -Wl,-rpath,$OPENRESTY_RPATH'" "--with-pcre-jit" "--with-http_ssl_module" "--with-http_realip_module" "--with-http_stub_status_module" "--with-http_v2_module" "-j$NPROC" ) if version_gte $NGINX_CORE_VER 1.11.4; then OPENRESTY_OPTS+=('--with-stream_realip_module') fi if version_gte $NGINX_CORE_VER 1.11.5; then OPENRESTY_OPTS+=('--with-stream_ssl_preread_module') fi if [ $KONG_NGINX_MODULE != 0 ]; then OPENRESTY_OPTS+=('--add-module=$DOWNLOAD_CACHE/lua-kong-nginx-module') fi if [ ! -z "$PCRE_VER" ]; then OPENRESTY_OPTS+=('--with-pcre=$PCRE_DOWNLOAD') fi OPENRESTY_OPTS+=(${NGINX_EXTRA_MODULES[@]}) if [ $DEBUG == 1 ]; then OPENRESTY_OPTS+=('--with-debug') OPENRESTY_OPTS+=('--with-luajit-xcflags="-DLUAJIT_USE_VALGRIND -DLUA_USE_ASSERT -DLUA_USE_APICHECK -DLUAJIT_USE_SYSMALLOC"') fi eval ./configure ${OPENRESTY_OPTS[*]} fi make -j$NPROC make -j$NPROC install DESTDIR=${OPENRESTY_DESTDIR} if [ $KONG_NGINX_MODULE != 0 ]; then pushd $DOWNLOAD_CACHE/lua-kong-nginx-module warn "installing library files for lua-kong-nginx-module..." make install LUA_LIB_DIR=$OPENRESTY_INSTALL/lualib popd fi popd succ "OpenResty $OPENRESTY_VER has been built successfully!" else succ "OpenResty $OPENRESTY_VER has been built successfully (cached)!" fi # Building LuaRocks if [ ! -z "$LUAROCKS_VER" ]; then if [ ! -f $LUAROCKS_INSTALL/bin/luarocks ]; then notice "Building LuaRocks..." pushd $LUAROCKS_DOWNLOAD if [ ! -f config.unix ]; then ./configure \ --prefix=$LUAROCKS_PREFIX \ --lua-suffix=jit \ --with-lua=$OPENRESTY_INSTALL/luajit \ --with-lua-include=$OPENRESTY_INSTALL/luajit/include/luajit-2.1 fi make build -j$NPROC make install DESTDIR=${LUAROCKS_DESTDIR} popd succ "LuaRocks $LUAROCKS_VER has been built successfully!" else succ "LuaRocks $LUAROCKS_VER has been built successfully (cached)!" fi fi succ "Build finished in $SECONDS seconds. Enjoy!" } parse_version() { [[ -z $1 ]] && fatal 'missing arg $1 when invoking parse_version()' [[ -z $2 ]] && fatal 'missing arg $2 when invoking parse_version()' local ver local subj=$1 if [[ $subj =~ ^[^0-9]*(.*) ]]; then subj=${BASH_REMATCH[1]} local re='^(-rc[0-9]+$)?[.]?([0-9]+|[a-zA-Z]+)?(.*)$' while [[ $subj =~ $re ]]; do if [[ ${BASH_REMATCH[1]} != "" ]]; then ver="$ver.${BASH_REMATCH[1]}" fi if [[ ${BASH_REMATCH[2]} != "" ]]; then ver="$ver.${BASH_REMATCH[2]}" fi subj="${BASH_REMATCH[3]}" if [[ $subj == "" ]]; then break fi done ver="${ver:1}" IFS='.' read -r -a $2 <<< "$ver" fi } parse_nginx_core_version() { [[ -z $1 ]] && fatal 'missing arg $1 when invoking parse_nginx_core_version()' local nginx_ver parse_version $1 nginx_ver echo "${nginx_ver[0]}.${nginx_ver[1]}.${nginx_ver[2]}" } version_eq() { local version_a version_b parse_version $1 version_a parse_version $2 version_b # Note that we are indexing on the b components, ie: 1.11.100 == 1.11 for index in "${!version_b[@]}"; do [[ "${version_a[index]}" != "${version_b[index]}" ]] && return 1 done return 0 } version_lt() { local version_a version_b parse_version $1 version_a parse_version $2 version_b for index in "${!version_a[@]}"; do if [[ ${version_a[index]} =~ ^[0-9]+$ ]]; then [[ "${version_a[index]}" -lt "${version_b[index]}" ]] && return 0 [[ "${version_a[index]}" -gt "${version_b[index]}" ]] && return 1 else [[ "${version_a[index]}" < "${version_b[index]}" ]] && return 0 [[ "${version_a[index]}" > "${version_b[index]}" ]] && return 1 fi done return 1 } version_gt() { (version_eq $1 $2 || version_lt $1 $2) && return 1 return 0 } version_lte() { (version_lt $1 $2 || version_eq $1 $2) && return 0 return 1 } version_gte() { (version_gt $1 $2 || version_eq $1 $2) && return 0 return 1 } canon_path() { if realpath -m -- $1 >&2 2>/dev/null; then realpath -m -- $1 else readlink -f -- $1 fi } n_proc() { if nproc >&2 2>/dev/null; then nproc elif [[ $OS == "Darwin" ]]; then sysctl -n hw.physicalcpu else echo "1" fi } show_usage() { echo "Build basic components (OpenResty, OpenSSL and LuaRocks) for Kong." echo "" echo "Usage: $0 [options...] -p --openresty --openssl " echo "" echo "Required arguments:" echo " -p, --prefix Location where components should be installed." echo " --openresty Version of OpenResty to build, such as 1.13.6.2." echo " --openssl Version of OpenSSL to build, such as 1.1.1c." echo "" echo "Optional arguments:" echo " --no-openresty-patches Do not apply openresty-patches while compiling OpenResty." echo " (Patching is enabled by default)" echo -e " \033[1;33mNOTE:\033[0m This option is mutually exclusive with --openresty-patches and --openresty-patches-dir." echo "" echo " --openresty-patches Specify an openresty-patches branch to use when applying patches." echo " (Defaults to \"master\")" echo -e " \033[1;33mNOTE:\033[0m This option is mutually exclusive with --openresty-patches-dir." echo "" echo " --openresty-patches-dir Specify the root directory of a local copy of openresty-patches" echo " to use instead of checking out a branch from the repository." echo -e " \033[1;33mNOTE:\033[0m This option is mutually exclusive with --openresty-patches." echo "" echo " --no-kong-nginx-module Do not include lua-kong-nginx-module while patching and compiling OpenResty." echo " (Patching and compiling is enabled by default for OpenResty > 1.13.6.1)" echo "" echo " --kong-nginx-module Specify a lua-kong-nginx-module branch to use when patching and compiling." echo " (Defaults to \"master\")" echo "" echo " --luarocks Version of LuaRocks to build, such as 3.1.2. If absent, LuaRocks" echo " will not be built." echo "" echo " --pcre Version of PCRE to build, such as 8.43. If absent, PCRE will" echo " not be build." echo "" echo " --add-module Path to additional NGINX module to be built. This option can be" echo " repeated and will be passed to NGINX's configure in the order" echo " they were specified." echo "" echo " --debug Disable compile-time optimizations and memory pooling for NGINX," echo " LuaJIT and OpenSSL to help debugging." echo "" echo " -j, --jobs Concurrency level to use when building." echo " (Defaults to number of CPU cores available: $NPROC)" echo "" echo " --work The working directory to use while compiling." echo " (Defaults to \"work\")" echo "" echo " -f, --force Build from scratch." echo -e " \033[1;31mWARNING:\033[0m This permanently removes everything inside the and directories." echo "" echo " -h, --help Show this message." } notice() { builtin echo -en "\033[1m" echo "NOTICE: $@" builtin echo -en "\033[0m" } succ() { builtin echo -en "\033[1;32m" echo "SUCCESS: $@" builtin echo -en "\033[0m" } warn() { builtin echo -en "\033[1;33m" echo "WARN: $@" builtin echo -en "\033[0m" } fatal() { builtin echo -en "\033[1;31m" echo "FATAL: $@" builtin echo -en "\033[0m" exit 1 } err() { builtin echo -en "\033[1;31m" echo "ERR: $@" builtin echo -en "\033[0m" exit 1 } if [[ $(basename $(canon_path $0)) == "kong-ngx-build" ]]; then main $@ fi # vi: ts=2 sts=2 sw=2 et