├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── build-nginx └── tests ├── test-config └── tests.bats /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | except: 3 | - hello-world-module 4 | language: bash 5 | before_install: 6 | - sudo add-apt-repository ppa:duggan/bats --yes 7 | - sudo apt-get update -qq 8 | - sudo apt-get install -qq bats 9 | install: 10 | - sudo apt-get install -qq bc 11 | script: 12 | - bats ./tests/tests.bats 13 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2017 Jay Caines-Gooby, @jaygooby, jay@gooby.org 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | # DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About `build-nginx` 2 | An nginx build tool to really simplify downloading and building specific versions of [nginx](http://nginx.org/) with different core and 3rd-party modules. 3 | 4 | [![Build Status](https://app.travis-ci.com/jaygooby/build-nginx.svg?branch=master)](https://app.travis-ci.com/github/jaygooby/build-nginx) 5 | 6 | [`ngx_http_hello_world_module`](https://github.com/jaygooby/build-nginx/tree/hello-world-module) courtesy of [perusio](https://github.com/perusio/nginx-hello-world-module) and [kolesar-andras](https://github.com/kolesar-andras/nginx-hello-world-module/tree/content-length) 7 | 8 | # TODO 9 | 10 | - [x] Work with git urls 11 | - [x] Work with archive urls (gzip & zipped tar releases) 12 | - [x] Use PCRE2 (you can also still use PCRE 1) `build-nginx` will do the right thing based on the dependency url you provide: 13 | * `build-nginx -d https://ftp.exim.org/pub/pcre/pcre-8.44.tar.gz` will use the old PCRE library 14 | 15 | * `build-nginx -d https://github.com/PCRE2Project/pcre2.git` will use the official PCRE2 github repo 16 | 17 | * `build-nginx -d https://github.com/PCRE2Project/pcre2.git@pcre2-10.40` will build the PCRE2 release tagged `pcre2-10.40` 18 | 19 | * `build-nginx -d https://sourceforge.net/projects/pcre/files/pcre2/10.37/pcre2-10.37.zip` will use the `10.37` zip at the unofficial sourceforge mirror 20 | - [ ] Handle dynamic nginx modules 21 | - [ ] Provide different example configurations 22 | - [ ] Update README with notes about: 23 | - [x] 64 bit MacOS Openssl builds 24 | - [ ] Use non-static Openssl on MacOS 25 | - [ ] How certain modules might implicitly enable the `--with-http_ssl_module` option 26 | - [ ] what takes precedence when you specify the same commandline option in a -k options file **and** on the commandline 27 | 28 | # Usage 29 | Basic usage: 30 | 31 | ``` 32 | ./build-nginx 33 | ``` 34 | 35 | Will `git --depth 1 --single-branch clone` the nginx `master` branch, configure and build it. Not so very useful... 36 | 37 | ## Specific nginx version and OpenSSL dependency, with non-core module 38 | How about getting nginx stable version 1.12.2 built with OpenSSL version 1.0.2l and HTTP/2 support? 39 | 40 | ``` 41 | ./build-nginx \ 42 | -n https://github.com/nginx/nginx.git@release-1.12.2 \ 43 | -d https://github.com/openssl/openssl.git@OpenSSL_1_0_2l \ 44 | -o --with-http_v2_module 45 | ``` 46 | 47 | Because you've specified OpenSSL as a dependency (`-d`) the nginx configure script automatically gets set with the `--with-openssl=` path. 48 | 49 | The `@` syntax lets you specify a release/tag/branch (or even specific commit - any [tree-ish reference](https://git-scm.com/docs/gitglossary#gitglossary-aiddeftree-ishatree-ishalsotreeish) should work). 50 | 51 | Here we're building nginx `master` with PCRE2 tagged at `pcre2-10.40` 52 | 53 | ``` 54 | build-nginx -d https://github.com/PCRE2Project/pcre2.git@pcre2-10.40 55 | ``` 56 | 57 | ### Building OpenSSL on 64bit macos 58 | You'll need to `export KERNEL_BITS=64` or call `build-nginx` like this: 59 | 60 | ``` 61 | KERNEL_BITS=64 ./build-nginx \ 62 | -n https://github.com/nginx/nginx.git@release-1.12.2 \ 63 | -d https://github.com/openssl/openssl.git@OpenSSL_1_0_2l 64 | ``` 65 | 66 | ## Archive URLs as well as git repos 67 | If you don't want to use a git repo, you can also use a source archive: 68 | 69 | ``` 70 | ./build-nginx -n http://nginx.org/download/nginx-1.13.6.tar.gz \ 71 | -d https://sourceforge.net/projects/pcre/files/pcre2/10.37/pcre2-10.37.zip \ 72 | -d https://www.openssl.org/source/openssl-1.0.2l.tar.gz 73 | ``` 74 | 75 | ## 3rd party modules 76 | You can also specify 3rd party modules using the same `git repo url @ version/tag/branch` string or archive url format. In the following example we haven't specifed an nginx version, so we clone from master, but we do clone a forked version of the nginx-upstream-fair module at version 0.1.3 77 | 78 | ``` 79 | ./build-nginx \ 80 | -m https://github.com/itoffshore/nginx-upstream-fair@0.1.3 81 | ``` 82 | 83 | Because we've specified a module (`-m`) the nginx configure script is automatically called with the `--add-module=` option, pointing to where the module was cloned. 84 | 85 | You could also use the official release archive URL: 86 | 87 | ``` 88 | ./build-nginx \ 89 | -m https://github.com/itoffshore/nginx-upstream-fair/archive/0.1.3.zip 90 | ``` 91 | 92 | ## 3rd party modules with a different config folder 93 | Some nginx modules don't have the `config` file in their root, and in these cases you need to let the nginx configure script know where to find it. Do this with an optional folder name after the version; in the example below we're using the [NAXSI](https://github.com/nbs-system/naxsi) project repository, specifying version `0.55.3` and letting the configure script know it needs to look in the NAXSI `naxsi_src` folder for the `config` file. 94 | 95 | ``` 96 | ./build-nginx \ 97 | -n https://github.com/nginx/nginx.git@release-1.12.2 \ 98 | -m https://github.com/nbs-system/naxsi.git@0.55.3,naxsi_src 99 | ``` 100 | 101 | ## Configuration files 102 | As well as specifying the options to `build-nginx` on the command line, you can save them into a configuration file, and pass this to the script instead: 103 | 104 | ``` 105 | ./build-nginx -k my-special-nginx-config 106 | ``` 107 | 108 | The config file is just a set of command-line options separated by newlines. Comments are permitted. Your `my-special-nginx-config` file might look like: 109 | 110 | ``` 111 | # nginx version 1.0 112 | -n https://github.com/nginx/nginx.git@release-1.0.0 113 | # it all lives in /opt/nginx 114 | -o --prefix=/opt/nginx 115 | -o --with-http_ssl_module # HTTPS 116 | -o --with-debug # helps us debug location directive errors 117 | # Use the OpenSSL in /opt 118 | -o --with-cc-opt=-I/opt/openssl/include 119 | -o --with-ld-opt=-L/opt/openssl/lib 120 | ``` 121 | 122 | ## Other options 123 | Call with `-h` to see the full set of options you can use. Currently these are: 124 | 125 | ``` 126 | -b If you want to build from an existing source repo 127 | 128 | -c If you only want to clone (download) and not build 129 | 130 | -f Lets you specify CFLAGS like -Wno-unused-variable 131 | 132 | -d Specify a git url and branch/tag/version for e.g. pcre 133 | 134 | -h Help 135 | 136 | -i Install. This will install to nginx's `configure` default location. To change, 137 | pass the nginx `--prefix` option like this: 138 | 139 | build-nginx -i -o --prefix=/usr/local/nginx 140 | 141 | -k Specify which config file to read this script's arguments from. 142 | The config file is a text file in which command line arguments 143 | can be written which then will be used as if they were written 144 | on the actual command line. 145 | 146 | -m Specify either an archive (.tar.gz, .tgz or .zip) 147 | URL or a git url, branch/tag/version and optional src 148 | folder where nginx looks for the module config file 149 | 150 | -n Optional nginx archive (.tar.gz, .tgz or .zip) URL or git repo url 151 | and/or optional branch/tag/version. Defaults to 152 | https://github.com/nginx/nginx.git@master. To specify just a 153 | branch/tag/version use @branch. To specify both, use git-url@branch 154 | 155 | -o To pass additional options to the nginx configure script 156 | 157 | -s The directory where this script will git clone 158 | nginx and any modules and dependencies it needs 159 | to build. Defaults to ~/.build-nginx 160 | ``` 161 | -------------------------------------------------------------------------------- /build-nginx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # MIT License 4 | # 5 | # Copyright (c) 2017 Jay Caines-Gooby, @jaygooby, jay@gooby.org 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | # DEALINGS IN THE SOFTWARE. 24 | # 25 | # Fetches and builds nginx, plus optional components like openssl, PCRE and any 26 | # 3rd-party modules you specify. 27 | # 28 | # Requires git version 1.7.10 or later (--single-branch option) 29 | # wget, tar and zip (to expand non-git archive files). 30 | # 31 | # Call with ./build-nginx to clone and compile the various modules and 32 | # nginx itself. Defaults to building in ~/.build-nginx 33 | # It will make this folder if it doesn't exist. 34 | # 35 | # To use a different build folder call the script like this: 36 | # ./build-nginx -s /some/folder 37 | # 38 | # To clone (download) but not compile the modules and nginx source: 39 | # ./build-nginx -d 40 | # 41 | # And to build from an existing ~/.build-nginx or $BUILD_DIR folder 42 | # ./build-nginx -b 43 | # 44 | # To specify additional options for the nginx configure script, use the 45 | # -o switch: 46 | # 47 | # ./build-nginx -o "--prefix=/usr/local --with-pcre=~/src/pcre-8.41" 48 | # 49 | # If you are building on a Mac and you're also providing an open-ssl source 50 | # directory, you'll need to ensure you export KERNEL_BITS=64 51 | set -eu 52 | 53 | # check dependencies 54 | for dependency in git tar wget zipinfo; do 55 | which $dependency > /dev/null || (echo "You need to have '$dependency' installed and in your \$PATH" >&2 && exit 1) 56 | done 57 | 58 | # Takes a line like https://github.com/nginx/nginx.git@release-1.12.1 59 | # or https://github.com/nbs-system/naxsi.git@0.55.3,naxsi_src 60 | # where the branch/tag/version is specified by the value after the @ 61 | # and optionally, where a directory is also specified after the , 62 | git_url_version_and_module_configure_dir() { 63 | echo "$(git_url $1) $(git_version $1) $(module_configure_dir $1)" 64 | } 65 | 66 | # Takes a line like https://github.com/nginx/nginx.git@release-1.12.1 67 | # and returns the git url 68 | git_url() { 69 | echo $1 | cut -d'@' -f1 | cut -d',' -f1 70 | } 71 | 72 | # Takes a line like https://github.com/nginx/nginx.git@release-1.12.1 73 | # and returns the treeish value, defaults to master if none present 74 | git_version() { 75 | version=$(echo $1 | cut -sd'@' -f2 | cut -d',' -f1) 76 | if [ -z "$version" ]; then 77 | echo master 78 | else 79 | echo $version 80 | fi 81 | } 82 | 83 | # Takes a line like https://github.com/nbs-system/naxsi.git@0.55.3,naxsi_src 84 | # and returns the value after the comma 85 | module_configure_dir() { 86 | echo $1 | cut -sd',' -f2 87 | } 88 | 89 | expand_error() { 90 | echo "Couldn't extract $1" && exit 1 91 | } 92 | 93 | # Given an archive file url (e.g. https://github.com/arut/nginx-rtmp-module/archive/master.zip) 94 | # turn this into a file-system safe filename we can save the archive to, 95 | # e.g https___github_com_arut_nginx-rtmp-module_archive_master.zip 96 | url_to_filename() { 97 | archive_url="$1" 98 | archive_path="$(dirname $archive_url)" 99 | archive_path="${archive_path//[.:\/]/_}" 100 | archive_file="${archive_path}_$(basename "$archive_url")" 101 | echo $archive_file 102 | } 103 | 104 | # Does the archive have a containing folder 105 | archive_has_containing_folder() { 106 | archive_file=$1 107 | 108 | case "$archive_file" in 109 | *tar.gz*) (tar -ztf "$archive_file" | head -1 | grep -q \/);; 110 | *tgz*) (tar -ztf "$archive_file" | head -1 | grep -q \/);; 111 | *zip) (zipinfo -Z1 "$archive_file" | head -1 | grep -q \/);; 112 | esac && echo $? 113 | } 114 | 115 | # Extract the tar archive $1 to folder $2 116 | # if it has a containing folder, then strip the initial folder in the archive 117 | untar_archive() { 118 | archive_file="$1" 119 | destination="$2" 120 | 121 | # echo archive_file: $archive_file 122 | # echo destination: $destination 123 | 124 | strip_containing_folder=$(archive_has_containing_folder "$1") 125 | 126 | # echo "strip_containing_folder? $strip_containing_folder" 127 | 128 | rm -rf "$destination" 129 | mkdir -p "$destination" 130 | if [ $strip_containing_folder == "0" ]; then 131 | tar -x --strip-components 1 -C "$destination" -f "$archive_file" 132 | else 133 | tar -xf -C "$destination" "$archive_file" 134 | fi 135 | } 136 | 137 | # Extract the zip archive $1 to folder $2 138 | # if it has a containing folder, then strip the initial folder in the archive 139 | unzip_archive() { 140 | archive_file="$1" 141 | destination="$2" 142 | 143 | # echo archive_file: $archive_file 144 | # echo destination: $destination 145 | 146 | strip_containing_folder=$(archive_has_containing_folder "$1") 147 | 148 | # echo "strip_containing_folder? $strip_containing_folder" 149 | 150 | rm -rf "$destination" 151 | unzip -u "$archive_file" -d "$destination" 152 | if [ "$strip_containing_folder" -eq "0" ]; then 153 | mv "$destination"/*/* "$destination" 154 | fi 155 | } 156 | 157 | # Fetches the archive url and expands it into a folder 158 | # named like the url it came from 159 | download_and_expand() { 160 | dir="$1" 161 | archive_url="$2" 162 | archive_file=$(url_to_filename "$archive_url") 163 | 164 | case "$archive_url" in 165 | *tar.gz*) archive_extension=".tar.gz" ;; 166 | *tgz*) archive_extension=".tgz" ;; 167 | *zip) archive_extension=".zip" ;; 168 | esac 169 | 170 | expanded_destination="$(basename $archive_file $archive_extension)" 171 | 172 | # echo expanded_destination: $expanded_destination 173 | 174 | wget "$archive_url" -O "$dir/$archive_file" 175 | case "$archive_url" in 176 | *tar.gz*) (untar_archive "$dir/$archive_file" "$dir/$expanded_destination") || expand_error $archive_url ;; 177 | *tgz*) (untar_archive "$dir/$archive_file" "$dir/$expanded_destination") || expand_error $archive_url ;; 178 | *zip) (unzip_archive "$dir/$archive_file" "$dir/$expanded_destination") || expand_error $archive_url ;; 179 | esac 180 | 181 | } 182 | 183 | clone_error() { 184 | echo "Couldn't checkout $1 at $2 into $3" 185 | exit 1 186 | } 187 | 188 | # Takes a git repository url as $2 and clones the specific 189 | # branch/tag/version requested in $3 as the folder named in $4 190 | # Does all this in the build folder which is passed in $1 191 | clone_at_version() { 192 | dir="$1" 193 | repo="$2" 194 | tag="${3:-master}" 195 | 196 | if [[ "$repo" =~ \.(zip|tgz|tar\.gz) ]]; then 197 | # don't clone, download and expand 198 | download_and_expand "$dir" "$repo" 199 | else 200 | # git clone 201 | cd "$dir" 202 | rm -rf "$4" 203 | git clone --depth 1 "$repo" --branch "$tag" --single-branch "$4" || clone_error "$repo" "$tag" "$4" 204 | fi 205 | } 206 | 207 | clone_dependencies() { 208 | dependencies=($1) 209 | for dependency in "${dependencies[@]}"; do 210 | echo "Adding $dependency" 211 | read -r repo_url branch <<<$(git_url_version_and_module_configure_dir $dependency) 212 | 213 | # Only set a branch if we're cloning from git 214 | # If we're extracting a tgz or zip, we don't care 215 | if [[ "$repo_url" =~ \.(zip|tgz|tar\.gz) ]]; then 216 | case "$repo_url" in 217 | *tar.gz*) extension=".tar.gz" ;; 218 | *tgz*) extension=".tgz" ;; 219 | *zip) extension=".zip" ;; 220 | esac 221 | source_dir="$build_dir/$(basename $(url_to_filename "$repo_url") $extension)" 222 | else 223 | branch=${branch:-master} 224 | source_dir="$build_dir/$(basename $repo_url .git)-$branch" 225 | fi 226 | 227 | echo "Fetching or cloning $dependency into $source_dir" 228 | 229 | if [ -z "${dont_clone:-}" ]; then 230 | # now git clone the repo at the branch/tag/version we want 231 | clone_at_version "$build_dir" "$repo_url" "$branch" "$source_dir" 232 | fi 233 | 234 | # Set the --with-openssl=DIR, --with-zlib=DIR, --with-openssl=DIR or 235 | # --with-libatomic=DIR nginx configure option if the relevant 236 | # library has been cloned 237 | case $repo_url in 238 | *pcre*) 239 | nginx_configure_options+=(--with-pcre="$source_dir") 240 | ensure_pcre2_has_a_makefile "$source_dir" 241 | ;; 242 | *zlib*) nginx_configure_options+=(--with-zlib="$source_dir") ;; 243 | *libatomic*) nginx_configure_options+=(--with-libatomic="$source_dir") ;; 244 | *openssl*) nginx_configure_options+=(--with-openssl="$source_dir") ;; 245 | esac 246 | 247 | done 248 | } 249 | 250 | # nginx expects to find a Makefile in the root of the PCRE2 library, 251 | # but the git cloned source version doesn't have this, and it needs generating 252 | # with autogen.sh. The .zip and tar archives don't have autogen.sh, but do 253 | # have a configure which will generate the Makefile 254 | ensure_pcre2_has_a_makefile() { 255 | source_dir="$1" 256 | cd "$source_dir" && (./autogen.sh || ./configure) 257 | } 258 | 259 | clone_nginx_and_modules() { 260 | items=0 261 | add_module="" 262 | sources="$1" 263 | 264 | # now iterate over the sources array, git cloning the specified 265 | # branch. If we're cloning any non-core nginx modules, we'll add them 266 | # to the nginx --configure options for inclusion in the build 267 | for source in "${sources[@]}"; do 268 | read -r repo_url branch add_module_dir <<<$(git_url_version_and_module_configure_dir $source) 269 | 270 | # Only set a branch if we're cloning from git 271 | # If we're extracting a tgz or zip, we don't care 272 | if [[ "$repo_url" =~ \.(zip|tgz|tar\.gz) ]]; then 273 | # echo "$repo_url looks like an archive URL" 274 | case "$repo_url" in 275 | *tar.gz*) extension=".tar.gz" ;; 276 | *tgz*) extension=".tgz" ;; 277 | *zip) extension=".zip" ;; 278 | esac 279 | source_dir="$build_dir/$(basename $(url_to_filename "$repo_url") $extension)" 280 | else 281 | branch=${branch:-master} 282 | source_dir="$build_dir/$(basename $repo_url .git)-$branch" 283 | fi 284 | 285 | # We don't want add nginx itself (it's always the first element 286 | # of the sources array) 287 | if [ $items -eq 0 ]; then 288 | nginx_dir=$source_dir 289 | else 290 | add_module="${add_module:-} --add-module=$source_dir/$add_module_dir" 291 | fi 292 | (( items += 1 )) 293 | 294 | if [ -z "${dont_clone:-}" ]; then 295 | # now git clone the repo at the branch/tag/version we want 296 | clone_at_version "$build_dir" "$repo_url" "$branch" "$source_dir" 297 | fi 298 | done 299 | } 300 | 301 | usage() { 302 | cat <<-USAGE 303 | -b If you want to build from an existing source repo 304 | 305 | -c If you only want to clone (download) and not build 306 | 307 | -d Specify a git url and branch/tag/version for e.g. pcre 308 | 309 | -f Lets you specify CFLAGS like -Wno-unused-variable 310 | 311 | -h Help 312 | 313 | -i install (without this, we'll only build) 314 | 315 | -k Specify which config file to read this script's arguments from. 316 | The config file is a text file in which command line arguments 317 | can be written which then will be used as if they were written 318 | on the actual command line. 319 | 320 | -m Specify either an archive (.tar.gz, .tgz or .zip) 321 | URL or a git url, branch/tag/version and optional src 322 | folder where nginx looks for the module config file 323 | 324 | -n Optional nginx archive (.tar.gz, .tgz or .zip) URL or git repo url 325 | and/or optional branch/tag/version. Defaults to 326 | https://github.com/nginx/nginx.git@master. To specify just a 327 | branch/tag/version use @branch. To specify both, use git-url@branch 328 | 329 | -o To pass additional options to the nginx configure script 330 | 331 | -s The directory where this script will git clone 332 | nginx and any modules and dependencies it needs 333 | to build. Defaults to ~/.build-nginx 334 | 335 | USAGE 336 | } 337 | 338 | # set some defaults 339 | build_dir="$HOME/.build-nginx" 340 | nginx_dir="" 341 | nginx_branch="master" 342 | nginx_repo_and_branch="https://github.com/nginx/nginx.git@${nginx_branch}" 343 | additional_nginx_modules="" 344 | cli_options="" 345 | make_install="" 346 | cflags="" 347 | 348 | declare -a nginx_configure_options 349 | declare -a cli_options 350 | declare -a sources 351 | 352 | # capture the commandline arguments - we need them later 353 | # if a -k file is being used 354 | options=( "$@" ) 355 | 356 | # Parse the options 357 | while getopts ":bcd:f:ik:m:n:o:s:" opt; do 358 | # echo $opt $OPTARG 359 | case ${opt} in 360 | # -b don't git clone any sources, just cd into the build dir and 361 | # recompile. Useful if you're making manual changes. 362 | b) dont_clone=1 ;; 363 | 364 | # -c download the git sources and then stop. Useful if you need to grab 365 | # the sources for caching, archiving etc. 366 | c) clone_only=1 ;; 367 | 368 | # -d optional dependencies such as pcre and openssl 369 | d) dependencies="${dependencies:-} ${OPTARG}" ;; 370 | 371 | # -f optional CFLAGS arguments like -Wno-unused-variable 372 | # passed to the compiler 373 | f) cflags="${CFLAGS:-} ${OPTARG}" ;; 374 | 375 | # -i to call nginx's make install 376 | i) make_install=true ;; 377 | 378 | # -k the file that specifes this script's command line options 379 | k) [ -e "$OPTARG" ] || (echo -e "Config file $OPTARG doesn't exist" && usage >&2 && exit 1) 380 | 381 | while IFS= read -r config; do 382 | # strip any comments whilst slurping in the file 383 | config=$(echo -e "${config}" | cut -d'#' -f1) 384 | if [ -n "${config}" ]; then 385 | cli_options+=("${config}") 386 | fi 387 | done < "${OPTARG}" 388 | # TODO: if there's a space between $1 and the quoted cli_options like 389 | # exec $0 "${cli_options[@]}" 390 | # then the options aren't passed to the command when you use the -k 391 | # option. There's a space that needs chopping 392 | # somewhere... 393 | # Also it seems that if you use -k and any other switches, the other 394 | # switches are ignored/overwritten by the -k call 395 | # 396 | # So remove the -k and its optarg from the options array 397 | # and retain the existing ones from the commandline 398 | # to save those from being wiped out when we call the options 399 | # set in the -k file 400 | delete=$OPTIND-2 401 | unset "options[$delete]" 402 | delete=$OPTIND-3 403 | unset "options[$delete]" 404 | options=${options:-""} 405 | # echo calling $0"${cli_options[@]}" "${options[@]}" 406 | # - this is still problematic as you also need to remove 407 | # or decide which cli argument overrides the same one set in the 408 | # -k file; e.g. if you specify -s in both the cli and the -k file 409 | # which -s takes precedence? 410 | exec $0"${cli_options[@]}" "${options[@]}" 411 | ;; 412 | 413 | # -m optional nginx modules to build 414 | # we append any previous -m args here so we process them all 415 | m) additional_nginx_modules="$additional_nginx_modules ${OPTARG}" ;; 416 | 417 | # -n 418 | n) nginx_repo_and_branch="${OPTARG}" ;; 419 | 420 | # -o additional options passed to the nginx configure script 421 | o) nginx_configure_options+=("$(echo $OPTARG | xargs)") ;; 422 | 423 | # -s the directory where we'll do the source build 424 | s) build_dir="$(echo $OPTARG | xargs)" ;; 425 | :) echo -e "Invalid option: -$OPTARG requires an argument" && usage >&2 426 | exit 1 427 | ;; 428 | \?) 429 | if [[ $OPTARG = "h" || $OPTARG = "?" ]]; then 430 | usage >&2 431 | exit 0 432 | else 433 | echo -e "Invalid option: -$OPTARG" && usage >&2 434 | exit 1 435 | fi 436 | ;; 437 | esac 438 | done 439 | 440 | # Make the build folder where all this will happen 441 | echo "Building nginx in $build_dir" 442 | mkdir -p "$build_dir" || (echo "Couldn't find or make $build_dir" && exit 1) 443 | 444 | # Clone any of the -d dependency libraries that have been requested 445 | if [ -n "${dependencies:-}" ]; then 446 | clone_dependencies "${dependencies[@]}" 447 | fi 448 | 449 | # Then clone nginx and the modules that were requested 450 | sources=("$nginx_repo_and_branch") 451 | for module in $additional_nginx_modules; do 452 | sources=("${sources[@]}" "$module") 453 | done 454 | clone_nginx_and_modules "${sources[@]}" 455 | 456 | # If we're only cloning and not building, we're done 457 | if [ -n "${clone_only:-}" ]; then 458 | exit 0 459 | fi 460 | 461 | # If we're cloning and building, clean, configure and build 462 | make clean || echo "No Makefile to clean" 463 | 464 | echo "cflags is $cflags" 465 | # If we cloned from git, we call ./auto/configure but if we 466 | # downloaded a release, we call ./configure 467 | if [ -f "$nginx_dir/auto/configure" ]; then 468 | conf="./auto/configure" 469 | else 470 | conf="./configure" 471 | fi 472 | 473 | if [ -n "${nginx_configure_options:-}" ]; then 474 | cd "$nginx_dir" && CFLAGS="$cflags" $conf "${nginx_configure_options[@]}" $add_module 475 | else 476 | cd "$nginx_dir" && CFLAGS="$cflags" $conf $add_module 477 | fi 478 | make && echo "Built nginx at $nginx_dir/objs/nginx" || echo "Build failed" 479 | if [ "$make_install" = true ]; then 480 | make install 481 | fi 482 | # and we're done 483 | -------------------------------------------------------------------------------- /tests/test-config: -------------------------------------------------------------------------------- 1 | -n https://github.com/nginx/nginx.git@release-1.21.5 2 | -o "--with-http_stub_status_module" 3 | -d https://github.com/PCRE2Project/pcre2.git@pcre2-10.40 4 | -------------------------------------------------------------------------------- /tests/tests.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "-h switch" { 4 | skip 5 | run ./build-nginx -h 6 | [ $status -eq 0 ] 7 | [[ "$output" =~ "-h Help" ]] 8 | } 9 | 10 | @test "-? switch" { 11 | skip 12 | run ./build-nginx -h 13 | [ $status -eq 0 ] 14 | [[ "$output" =~ "-h Help" ]] 15 | } 16 | 17 | @test "Invalid switch" { 18 | skip 19 | run ./build-nginx -X 20 | 21 | [ $status -ne 0 ] 22 | [[ "$output" =~ "Invalid option" ]] 23 | } 24 | 25 | @test "-f CFLAGS switch" { 26 | # skip 27 | run ./build-nginx -f -Wno-unused-variable 28 | [ $status -eq 0 ] 29 | [[ "$output" =~ "no-unused-variable" ]] 30 | } 31 | 32 | @test "Installing with -i switch" { 33 | skip 34 | installdir="$(mktemp -d)" 35 | run ./build-nginx -i -o --prefix="$installdir" 36 | [ $status -eq 0 ] 37 | [[ "$output" =~ "cp objs/nginx '$installdir/sbin/nginx'" ]] 38 | } 39 | 40 | @test "Clone nginx master" { 41 | skip 42 | builddir="$(mktemp -d)" 43 | run ./build-nginx -s "$builddir" -c 44 | [ $status -eq 0 ] 45 | [ -d "$builddir/nginx-master" ] 46 | } 47 | 48 | @test "Clone nginx at specific version" { 49 | skip 50 | builddir="$(mktemp -d)" 51 | run ./build-nginx -s "$builddir" -n https://github.com/nginx/nginx.git@release-1.12.2 52 | [ $status -eq 0 ] 53 | [ -d "$builddir/nginx-release-1.12.2" ] 54 | run "$builddir/nginx-release-1.12.2/objs/nginx" -V 55 | [[ "$output" =~ "nginx version: nginx/1.12.2" ]] 56 | } 57 | 58 | @test "Build from an existing cloned source directory" { 59 | skip 60 | builddir="$(mktemp -d)" 61 | run ./build-nginx -s "$builddir" -c 62 | run ./build-nginx -s "$builddir" -b 63 | [ -d "$builddir/nginx-master" ] 64 | [ $status -eq 0 ] 65 | [ -f "$builddir/nginx-master/objs/nginx" ] 66 | } 67 | 68 | @test "Clone and build nginx with a 3rd party module from a specific branch" { 69 | skip 70 | builddir="$(mktemp -d)" 71 | run ./build-nginx -s "$builddir" -m https://github.com/jaygooby/build-nginx.git@hello-world-module 72 | [ $status -eq 0 ] 73 | [[ "$output" =~ "objs/addon/build-nginx-hello-world-module/ngx_http_hello_world_module.o" ]] 74 | } 75 | 76 | @test "build with PCRE2 git url" { 77 | skip 78 | builddir="$(mktemp -d)" 79 | run ./build-nginx -s "$builddir" -d https://github.com/PCRE2Project/pcre2.git@pcre2-10.40 80 | [ $status -eq 0 ] 81 | run "$builddir/nginx-master/objs/nginx" -V 82 | [[ "$output" =~ "--with-pcre=" ]] 83 | } 84 | 85 | @test "build with PCRE2 zip archive" { 86 | skip 87 | builddir="$(mktemp -d)" 88 | run ./build-nginx -s "$builddir" -d https://sourceforge.net/projects/pcre/files/pcre2/10.37/pcre2-10.37.zip 89 | [ $status -eq 0 ] 90 | run "$builddir/nginx-master/objs/nginx" -V 91 | [[ "$output" =~ "--with-pcre=" ]] 92 | } 93 | 94 | @test "build with older PCRE 1 library" { 95 | skip 96 | builddir="$(mktemp -d)" 97 | run ./build-nginx -s "$builddir" -d https://ftp.exim.org/pub/pcre/pcre-8.44.tar.gz 98 | [ $status -eq 0 ] 99 | run "$builddir/nginx-master/objs/nginx" -V 100 | [[ "$output" =~ "--with-pcre=" ]] 101 | } 102 | 103 | @test "nginx with openssl 1.0.2l" { 104 | skip 105 | builddir="$(mktemp -d)" 106 | run ./build-nginx -s "$builddir" -d https://github.com/openssl/openssl.git@OpenSSL_1_0_2l 107 | [ $status -eq 0 ] 108 | run "$builddir/nginx-master/objs/nginx" -V 109 | [[ "$output" =~ "--with-openssl=" ]] 110 | } 111 | 112 | @test "nginx with openssl and zlib" { 113 | skip 114 | builddir="$(mktemp -d)" 115 | run ./build-nginx -s "$builddir" -d https://github.com/openssl/openssl.git -d https://github.com/madler/zlib.git 116 | [ $status -eq 0 ] 117 | run "$builddir/nginx-master/objs/nginx" -V 118 | [[ "$output" =~ "--with-openssl=" ]] 119 | [[ "$output" =~ "--with-zlib=" ]] 120 | } 121 | 122 | @test "Build nginx from an archive URL" { 123 | skip 124 | builddir="$(mktemp -d)" 125 | echo $builddir >&2 126 | run ./build-nginx -s "$builddir" -n http://nginx.org/download/nginx-1.13.6.tar.gz 127 | [ $status -eq 0 ] 128 | [ -d "$builddir/http___nginx_org_download_nginx-1.13.6" ] 129 | run "$builddir/http___nginx_org_download_nginx-1.13.6/objs/nginx" -V 130 | [[ "$output" =~ "nginx version: nginx/1.13.6" ]] 131 | } 132 | 133 | @test "Build nginx from an archive URL and with a module from a git repo URL" { 134 | skip 135 | builddir="$(mktemp -d)" 136 | run ./build-nginx -s "$builddir" -n http://nginx.org/download/nginx-1.13.6.tar.gz -m https://github.com/jaygooby/build-nginx.git@hello-world-module 137 | [ $status -eq 0 ] 138 | [ -d "$builddir/http___nginx_org_download_nginx-1.13.6" ] 139 | run "$builddir/http___nginx_org_download_nginx-1.13.6/objs/nginx" -V 140 | [[ "$output" =~ "nginx version: nginx/1.13.6" ]] 141 | } 142 | 143 | @test "Specify a module config folder using a git url" { 144 | skip 145 | builddir="$(mktemp -d)" 146 | run ./build-nginx -s "$builddir" -m https://github.com/nbs-system/naxsi.git,naxsi_src 147 | [ $status -eq 0 ] 148 | [[ "$output" =~ "objs/addon/naxsi_src/naxsi_runtime.o" ]] 149 | } 150 | 151 | @test "Specify a module config folder using an archive url" { 152 | skip 153 | builddir="$(mktemp -d)" 154 | run ./build-nginx -s "$builddir" -m https://github.com/nbs-system/naxsi/archive/0.55.3.tar.gz,naxsi_src 155 | [ $status -eq 0 ] 156 | [[ "$output" =~ "objs/addon/naxsi_src/naxsi_runtime.o" ]] 157 | } 158 | 159 | @test "Use just a config file" { 160 | skip 161 | builddir="$HOME/.build-nginx" 162 | run ./build-nginx -k tests/test-config 163 | [ $status -eq 0 ] 164 | run "$builddir/nginx-release-1.21.5/objs/nginx" -V 165 | [[ "$output" =~ "--with-http_stub_status_module" ]] 166 | [[ "$output" =~ "--with-pcre" ]] 167 | } 168 | 169 | @test "Use a config file with additional commandline options" { 170 | skip 171 | builddir="$(mktemp -d)" 172 | run ./build-nginx -s "$builddir" -k tests/test-config -m https://github.com/jaygooby/build-nginx.git@hello-world-module 173 | [ $status -eq 0 ] 174 | [[ "$output" =~ "objs/addon/build-nginx-hello-world-module/ngx_http_hello_world_module.o" ]] 175 | run "$builddir/nginx-release-1.21.5/objs/nginx" -V 176 | [[ "$output" =~ "nginx version: nginx/1.21.5" ]] 177 | [[ "$output" =~ "--with-http_stub_status_module" ]] 178 | [[ "$output" =~ "--with-pcre" ]] 179 | } 180 | --------------------------------------------------------------------------------