Added script to create installable packages for third-party modules.
Demonstration of how to use the NGINX pkg-oss tooling to compile
and package a dynamic module for NGINX and NGINX Plus.
diff --git a/build_module.sh b/build_module.sh
new file mode 100755
index 0000000..58348f9
--- /dev/null
+++ b/build_module.sh
@@ -0,0 +1,367 @@
+#!/usr/bin/env bash
+#
+# build_module.sh (c) NGINX, Inc. [v0.10 27-Apr-2017] Liam Crilly <liam.crilly@nginx.com>
+#
+# This script supports apt(8) and yum(8) package managers. Installs the minimum
+# necessary prerequisite packages to build 3rd party modules for NGINX Plus.
+# Obtains source for module and NGINX OSS, prepares for pkg-oss tool. Inspects
+# module configuration and attempts to rewrite for dynamic build if necessary.
+# Obtains pkg-oss tool, creates packaging files and copies in module source.
+#
+# CHANGELOG
+# v0.10 [27-Apr-2017] Fixed postinstall banner, improved .so filename detection,
+# -v option for specifying OSS build/version
+# v0.9 [10-Apr-2017] @defan patch, improved postinstall banner, added disclaimer
+# v0.8 [30-Mar-2017] Package version is now tied to base OSS version instead of 0.01
+# v0.7 [29-Mar-2017] Added RPM packaging, flexible command line options with defaults
+# v0.6 [16-Feb-2017] Using pkg-oss tool instead of only compiling .so files
+
+cat << __EOF__
+
+DISCLAIMER DISCLAIMER DISCLAIMER DISCLAIMER DISCLAIMER DISCLAIMER DISCLAIMER
+
+ This script is provided as a demonstration of how to use the NGINX pkg-oss
+ tooling to compile and package a dynamic module for NGINX and NGINX Plus.
+ It will produce an installable package with the correct dependency on the
+ NGINX version used for the build so that upgrades will not lead to mismatch
+ between NGINX and the module. When using this script please bear in mind:
+ - It will not work for every module, check for module prerequisites.
+ - The installable packages are not intended for redistribution.
+ - Seamless upgrades with dependency matching require a yum/apt repository.
+
+__EOF__
+
+#
+# Check command line parameters
+#
+ME=`basename $0`
+if [ $# -eq 0 ]; then
+ echo "USAGE: $ME [options] <URL | path to module source>"
+ echo ""
+ echo " URL may be Github clone or download link, otherwise 'tarball' is assumed."
+ echo " Options:"
+ echo " -n | --nickname <word> # Used for packaging, lower case alphanumeric only"
+ echo " -s | --skip-depends # Skip dependecies check/install"
+ echo " -f | --force-dynamic # Attempt to convert static configuration to dynamic module"
+ echo " -r <NGINX Plus release number> # Build against the corresponding OSS version for this release"
+ echo " -v [NGINX OSS version number] # Build against this OSS version [current mainline] (default)"
+ echo ""
+ exit 1
+fi
+
+#
+# Process command line options
+#
+CHECK_DEPENDS=1
+DO_DYNAMIC_CONVERT=0
+MODULE_NAME=""
+BUILD_PLATFORM=OSS
+while [ $# -gt 1 ]; do
+ case "$1" in
+ "-s" | "--skip-depends")
+ CHECK_DEPENDS=0
+ shift
+ ;;
+ "-f" | "--force-dynamic")
+ DO_DYNAMIC_CONVERT=1
+ shift
+ ;;
+ "-n" | "--nickname" )
+ MODULE_NAME=$2
+ shift; shift
+ ;;
+ "-r")
+ BUILD_PLATFORM=Plus
+ if [ `echo -n $2 | tr -d '[0-9p]' | wc -c` -gt 0 ]; then
+ echo "$ME: ERROR: NGINX Plus release must be in the format NN[pN] - quitting"
+ exit 1
+ fi
+ PLUS_REL=$2
+ shift; shift
+ ;;
+ "-v")
+ BUILD_PLATFORM=OSS
+ if [ `echo -n .$2 | tr -d '[0-9\.]' | wc -c` -eq 0 ]; then
+ OSS_VER=$2
+ shift
+ fi
+ shift
+ ;;
+ *)
+ echo "$ME: ERROR: Invalid command line option ($1) - quitting"
+ exit 1
+ ;;
+ esac
+done
+
+#
+# Locate/select package manager and configure
+#
+if [ `whereis yum | grep -c "^yum: /"` -eq 1 ]; then
+ PKG_MGR=yum
+ NGINX_PACKAGES="pcre-devel zlib-devel openssl-devel"
+ DEVEL_PACKAGES="rpm-build"
+ PACKAGING_DIR=rpm/SPECS
+ PACKAGE_SOURCES_DIR=../SOURCES
+ FIND_PKGS_CMD="find ${HOME}/rpmbuild/RPMS/ -maxdepth 2 -type f -name "*.rpm" -print"
+ FIND_SO_CMD="find ${HOME}/rpmbuild/BUILD/ -type f -name "*.so" -print"
+elif [ `whereis apt-get | grep -c "^apt-get: /"` -eq 1 ]; then
+ PKG_MGR=apt-get
+ NGINX_PACKAGES="libpcre3-dev zlib1g-dev libssl-dev"
+ DEVEL_PACKAGES="devscripts debhelper dpkg-dev quilt lsb-release"
+ PACKAGING_DIR=debian
+ PACKAGE_SOURCES_DIR=extra
+ FIND_PKGS_CMD="find ${HOME}/debuild/ -maxdepth 1 -type f -name "*.deb" -print"
+ FIND_SO_CMD="find ${HOME}/debuild/ -maxdepth 9 -type f -name "*.so" -print"
+else
+ echo "$ME: ERROR: Could not locate a supported package manager - quitting"
+ exit 1
+fi
+
+if [ $CHECK_DEPENDS = 1 ]; then
+ echo "$ME: INFO: testing sudo"
+ sudo pwd > /dev/null
+ if [ $? -ne 0 ]; then
+ echo "ERROR: sudo failed. If you do not have sudo credentials then try using the '--skip-depends' option. Quitting."
+ exit 1
+ fi
+
+ echo "$ME: INFO: checking for dependent packages"
+ CORE_PACKAGES="gcc make unzip"
+ if [ "$BUILD_PLATFORM" = "OSS" ]; then
+ CORE_PACKAGES="$CORE_PACKAGES mercurial"
+ fi
+ if [ "${1##*.}" == "git" ]; then
+ CORE_PACKAGES="$CORE_PACKAGES git"
+ fi
+ sudo $PKG_MGR install $CORE_PACKAGES $NGINX_PACKAGES $DEVEL_PACKAGES
+fi
+
+#
+# Ask for a nickname if we did't get one on the command line
+#
+if [ "$MODULE_NAME" = "" ]; then
+ #
+ # Construct a reasonable nickname from the module source location
+ #
+ MODULE_NAME=`basename $1 | tr '[:blank:][:punct:]' '\n' | tr '[A-Z]' '[a-z]' | grep -ve nginx -e ngx -e http -e stream -e module -e plus -e tar -e zip -e gz -e git | tr -d '\n'`
+ read -p "$ME: INPUT: Enter module nickname [$MODULE_NAME]: "
+ if [ "$REPLY" != "" ]; then
+ MODULE_NAME=$REPLY
+ fi
+fi
+
+#
+# Sanitize module nickname (this is a debbuild requirement, probably needs to check for more characters)
+#
+MODULE_NAME_CLEAN=`echo $MODULE_NAME | tr '[A-Z]' '[a-z]' | tr -d '[_\-\.\t ]'`
+if [ $MODULE_NAME_CLEAN != $MODULE_NAME ]; then
+ echo "$ME: WARNING: Removed illegal characters from module nickname - using $MODULE_NAME_CLEAN"
+ read -p "$ME: INPUT: Confirm module nickname [$MODULE_NAME_CLEAN]: " MODULE_NAME
+ if [ "$MODULE_NAME" = "" ]; then
+ MODULE_NAME=$MODULE_NAME_CLEAN
+ fi
+fi
+
+#
+# Create temporary build area, with working copy of module source
+#
+BUILD_DIR=/tmp/$ME.$$
+MODULE_DIR=$BUILD_DIR/$MODULE_NAME
+echo "$ME: INFO: Creating $BUILD_DIR build area"
+mkdir $BUILD_DIR
+
+if [ -d $1 ]; then
+ mkdir -v $MODULE_DIR
+ echo "$ME: INFO: Building $MODULE_NAME from $MODULE_DIR"
+ cp -a $1/* $MODULE_DIR
+else
+ #
+ # Module sources string is not a local directory so assume it is a URL.
+ # Obtain the sources in the best way for the suffix provided.
+ #
+ case "${1##*.}" in
+ "git")
+ echo "$ME: INFO: Cloning module source"
+ git clone --recursive $1 $MODULE_DIR
+ ;;
+ "zip")
+ echo "$ME: INFO Downloading module source"
+ wget -O $BUILD_DIR/module.zip $1
+ ARCHIVE_DIR=`zipinfo -1 $BUILD_DIR/module.zip | head --lines=1 | cut -f1 -d/`
+ unzip $BUILD_DIR/module.zip -d $BUILD_DIR
+ mv $BUILD_DIR/$ARCHIVE_DIR $MODULE_DIR
+ ;;
+ *)
+ echo "$ME: INFO Downloading module source"
+ # Assume tarball of some kind
+ wget -O $BUILD_DIR/module.tgz $1
+ ARCHIVE_DIR=`tar tfz $BUILD_DIR/module.tgz | head --lines=1 | cut -f1 -d/`
+ cd $BUILD_DIR
+ tar xfz module.tgz
+ mv $ARCHIVE_DIR $MODULE_DIR
+ cd -
+ ;;
+ esac
+fi
+
+#
+# Check the module sources look OK
+#
+if [ ! -f $MODULE_DIR/config ]; then
+ echo "$ME: ERROR: Cannot locate module config file - quitting"
+ exit 1
+fi
+
+#
+# Check/convert module config
+#
+if [ `grep -c "\.[[:space:]]auto/module" $MODULE_DIR/config` -eq 0 ]; then
+ if [ $DO_DYNAMIC_CONVERT = 1 ]; then
+ echo "$ME: WARNING: This is a static module, attempting to convert to dynamic (experimental)"
+ grep -ve HTTP_MODULES -e STREAM_MODULES -e NGX_ADDON_SRCS $MODULE_DIR/config > $MODULE_DIR/config.dynamic
+ echo "ngx_module_name=`grep ngx_addon_name= $MODULE_DIR/config | cut -f2 -d=`" >> $MODULE_DIR/config.dynamic
+ if [ `grep -c "HTTP_AUX_FILTER_MODULES=" $MODULE_DIR/config` -gt 0 ]; then
+ echo "ngx_module_type=HTTP_AUX_FILTER" >> $MODULE_DIR/config.dynamic
+ elif [ `grep -c "STREAM_MODULES=" $MODULE_DIR/config` -gt 0 ]; then
+ echo "ngx_module_type=Stream" >> $MODULE_DIR/config.dynamic
+ else
+ echo "ngx_module_type=HTTP" >> $MODULE_DIR/config.dynamic
+ fi
+ echo "ngx_module_srcs=\"`grep NGX_ADDON_SRCS= $MODULE_DIR/config | cut -f2 -d\\" | sed -e 's/^\$NGX_ADDON_SRCS \(\$ngx_addon_dir\/.*$\)/\1/'`\"" >> $MODULE_DIR/config.dynamic
+ echo ". auto/module" >> $MODULE_DIR/config.dynamic
+ mv $MODULE_DIR/config $MODULE_DIR/config.static
+ cp $MODULE_DIR/config.dynamic $MODULE_DIR/config
+ else
+ echo "$ME: ERROR: This is a static module and should be updated to dynamic configuration. To attempt automatic conversion to dynamic module configuration use the '--force-dynamic' option. This will not modify the original configuration. Quitting."
+ exit 1
+ fi
+fi
+
+#
+# Get the internal module name(s) from the module config so we can write
+# the .so files into the postinstall banner.
+#
+touch $BUILD_DIR/postinstall.txt
+for MODULE_SO_NAME in $(grep ngx_module_name= $MODULE_DIR/config | cut -f2 -d= | cut -f2 -d\"); do
+ if [ "`echo $MODULE_SO_NAME | cut -c1`" = "$" ]; then
+ # Dereference variable
+ SOURCE_VAR=`echo $MODULE_SO_NAME | cut -f2 -d\$`
+ MODULE_SO_NAME=`grep $SOURCE_VAR= $MODULE_DIR/config | cut -f2 -d= | cut -f2 -d\"`
+ fi
+ # Only write load_module line when no backslash present (can't cope with multi-line values)
+ echo $MODULE_SO_NAME | grep -c '\\' > /dev/null
+ if [ $? -eq 1 ]; then
+ echo " load_module modules/$MODULE_SO_NAME.so;" >> $BUILD_DIR/postinstall.txt
+ fi
+done
+if [ ! -s $BUILD_DIR/postinstall.txt ]; then
+ # Didn't find any .so names so this is a final attempt to extract from config file
+ MODULE_SO_NAME=`grep ngx_addon_name= $MODULE_DIR/config | cut -f2 -d= | cut -f2 -d\"`
+ echo " load_module modules/$MODULE_SO_NAME.so;" >> $BUILD_DIR/postinstall.txt
+fi
+
+#
+# Get NGINX OSS packaging tool
+#
+echo "$ME: INFO: Downloading NGINX packaging tool"
+cd $BUILD_DIR
+if [ "$BUILD_PLATFORM" = "OSS" ]; then
+ if [ "$OSS_VER" != "" ]; then
+ MERCURIAL_TAG="-r $OSS_VER-1"
+ fi
+ hg clone $MERCURIAL_TAG http://hg.nginx.org/pkg-oss
+ cd pkg-oss/$PACKAGING_DIR
+else
+ wget -O - http://hg.nginx.org/pkg-oss/archive/target-plus-r$PLUS_REL.tar.gz | tar xfz -
+ cd pkg-oss-target-plus-r$PLUS_REL/$PACKAGING_DIR
+fi
+if [ $? -ne 0 ]; then
+ echo "$ME: ERROR: Unable to obtain NGINX packaging tool - quitting"
+ exit 1
+fi
+
+#
+# Archive the module source for use with packaging tool using the base OSS version
+#
+VERSION=`grep "^BASE_VERSION=" Makefile | cut -f2 -d= | tr -d "[:blank:]"`
+echo "$ME: INFO: Archiving module source for $VERSION"
+cd $BUILD_DIR
+mv $MODULE_NAME $MODULE_NAME-$VERSION
+tar cf - $MODULE_NAME-$VERSION | gzip -1 > $OLDPWD/$PACKAGE_SOURCES_DIR/$MODULE_NAME-$VERSION.tar.gz
+cd -
+
+echo "$ME: INFO: Creating changelog"
+if [ "$PKG_MGR" = "yum" ]; then
+ echo "* `date '+%a %b %d %Y'` Build Script <build.script@example.com>" > nginx-module-$MODULE_NAME.changelog.in
+ echo "- initial version of $MODULE_NAME module" >> nginx-module-$MODULE_NAME.changelog.in
+else
+ cat << __EOF__ > nginx-module-$MODULE_NAME.changelog.in
+nginx-module-$MODULE_NAME (${VERSION}) %%CODENAME%%; urgency=low
+
+ * initial release of $MODULE_NAME module for nginx
+
+ -- Build Script <build.script@example.com> `date -R`
+__EOF__
+fi
+
+echo "$ME: INFO: Creating module Makefile"
+cat << __EOF__ > Makefile.module-$MODULE_NAME
+MODULES=$MODULE_NAME
+
+MODULE_PACKAGE_VENDOR= Build Script <build.script@example.com>
+MODULE_PACKAGE_URL= https://www.nginx.com/blog/compiling-dynamic-modules-nginx-plus/
+
+MODULE_SUMMARY_$MODULE_NAME= $MODULE_NAME dynamic module
+MODULE_VERSION_$MODULE_NAME= $VERSION
+MODULE_RELEASE_$MODULE_NAME= 1
+MODULE_CONFARGS_$MODULE_NAME= --add-dynamic-module=\$(MODSRC_PREFIX)$MODULE_NAME-$VERSION
+MODULE_SOURCES_$MODULE_NAME= $MODULE_NAME-$VERSION.tar.gz
+
+define MODULE_POST_$MODULE_NAME
+cat <<BANNER
+----------------------------------------------------------------------
+
+The \$(MODULE_SUMMARY_$MODULE_NAME) for nginx has been installed.
+To enable this module, add the following to /etc/nginx/nginx.conf
+and reload nginx:
+
+`uniq $BUILD_DIR/postinstall.txt`
+
+----------------------------------------------------------------------
+BANNER
+endef
+export MODULE_POST_$MODULE_NAME
+__EOF__
+
+#
+# Build!
+#
+echo "$ME: INFO: Building"
+make prepare-build-env
+if [ $? -ne 0 ]; then
+ echo "$ME: ERROR: Unable to prepare build environment - quitting"
+ exit 1
+fi
+
+if [ "$PKG_MGR" = "yum" ]; then
+ cd ~/rpmbuild/SPECS
+else
+ cd ~/debuild/nginx-$VERSION/debian
+fi
+
+if [ "$BUILD_PLATFORM" = "Plus" ]; then
+ MODULE_TARGET=plus make module-$MODULE_NAME
+else
+ make module-$MODULE_NAME
+fi
+if [ $? -ne 0 ]; then
+ echo "$ME: ERROR: Build failed"
+else
+ echo "$ME: INFO: Module binaries created"
+ $FIND_SO_CMD
+ echo "$ME: INFO: Module packages created"
+ $FIND_PKGS_CMD
+ echo "$ME: INFO: Removing $BUILD_DIR"
+ rm -fr $BUILD_DIR
+fi