#!/usr/bin/env perl use 5.006; use strict; use warnings; use File::Spec; sub shell ($@); sub env ($$); sub cd ($); sub auto_complete ($); sub usage ($); my (@make_cmds, @make_install_cmds); my $root_dir = `pwd`; chomp $root_dir; my $OS = $^O; my $ngx_dir; for my $opt (@ARGV) { if ($opt =~ /^--platform=(.*)/) { $OS = $1; undef $opt; } } my ($platform, $on_solaris); if ($OS =~ /solaris|sunos/i) { $platform = 'solaris'; $on_solaris = $platform; } elsif ($OS eq 'linux') { $platform = $OS; } elsif ($OS eq 'MSWin32') { die "MS Windows not supported. Abort.\n"; } elsif ($OS =~ /^(?:MacOS|darwin|rhapsody)$/) { $platform = 'macosx'; } elsif ($OS eq 'freebsd') { $platform = $OS; } elsif ($OS =~ /^(?:openbsd|netbsd)$/) { $platform = 'bsd'; } else { $platform = 'posix'; } my @modules = ( [ndk => 'ngx_devel_kit'], [http_iconv => 'iconv-nginx-module', 'disabled'], [http_echo => 'echo-nginx-module'], [http_xss => 'xss-nginx-module'], [http_set_misc => 'set-misc-nginx-module'], [http_form_input => 'form-input-nginx-module'], [http_encrypted_session => 'encrypted-session-nginx-module'], [http_drizzle => 'drizzle-nginx-module', 'disabled'], [http_postgres => 'ngx_postgres', 'disabled'], [http_lua => 'ngx_lua'], [http_headers_more => 'headers-more-nginx-module'], [http_srcache => 'srcache-nginx-module'], [http_array_var => 'array-var-nginx-module'], [http_memc => 'memc-nginx-module'], [http_redis2 => 'redis2-nginx-module'], [http_upstream_keepalive => 'upstream-keepalive-nginx-module'], [http_auth_request => 'auth-request-nginx-module'], [http_rds_json => 'rds-json-nginx-module'], ); my $without_resty_mods_regex; { my $s = '^--without-(' . join('|', map { $_->[0] } grep { @$_ == 2 && $_->[0] =~ /^http_/ } @modules ) . ')_module$'; #warn "without regex: $s"; $without_resty_mods_regex = qr/$s/; } my $with_resty_mods_regex; { my $s = '^--with-(' . join('|', map { $_->[0] } grep { @$_ == 3 && $_->[0] =~ /^http_/ } @modules ) . ')_module$'; #warn "with regex: $s"; $with_resty_mods_regex = qr/$s/; } my $prefix = '/usr/local/openresty'; my %resty_opts; my $dry_run; my @ngx_rpaths; my $cc; my (@ngx_opts, @ngx_cc_opts, @ngx_ld_opts); for my $opt (@ARGV) { next unless defined $opt; if ($opt =~ /^--with-cc=(.+)/) { $cc = $1; push @ngx_opts, $opt; next; } if ($opt eq '--dry-run') { $dry_run = 1; next; } if ($opt =~ /^--with-make=(.*)/) { $resty_opts{make} = $1; next; } if ($opt =~ /^--prefix=(.*)/) { $prefix = $1; } elsif ($opt eq '--without-lua51') { $resty_opts{no_lua} = 1; } elsif ($opt eq '--without-lua_cjson') { $resty_opts{no_lua_cjson} = 1; } elsif ($opt eq '--with-debug') { $resty_opts{debug} = 1; } elsif ($opt eq '--help') { usage 0; } elsif ($opt =~ /^--with-cc-opt=(.*)/) { push @ngx_cc_opts, $1; } elsif ($opt =~ /^--with-ld-opt=(.*)/) { push @ngx_ld_opts, $1; } elsif ($opt =~ $without_resty_mods_regex) { #die "no_$1\n"; $resty_opts{"no_$1"} = 1; } elsif ($opt eq '--without-ngx_devel_kit_module') { $resty_opts{no_ndk} = 1; } elsif ($opt =~ $with_resty_mods_regex) { $resty_opts{"$1"} = 1; } elsif ($opt eq '--with-luajit') { $resty_opts{luajit} = 1; } elsif ($opt =~ /^--with-libdrizzle=(.*)/) { $resty_opts{libdrizzle} = $1; } elsif ($opt =~ /^--with-libpq=(.*)/) { $resty_opts{libpq} = $1; if ($resty_opts{pg_config}) { die "--with-libpq is not allowed when ", "--with-pg_config is already specified.\n"; } } elsif ($opt =~ /^--with-pg_config=(.*)/) { $resty_opts{pg_config} = $1; if ($resty_opts{libpq}) { die "--with-pg_config is not allowed when ", "--with-libpq is already specified.\n"; } } elsif ($opt eq '--with-no-pool-patch') { $resty_opts{no_pool} = 1; } elsif ($opt eq '--with-http_ssl_module') { $resty_opts{http_ssl} = 1; push @ngx_opts, $opt; } elsif ($opt eq '--without-http_ssl_module') { $resty_opts{no_http_ssl} = 1; } elsif ($opt =~ /^--\w.*/) { push @ngx_opts, $opt; } else { die "Invalid option $opt\n"; } } print "platform: $platform ($OS)\n"; my $ngx_prefix = "$prefix/nginx"; my $postamble = ''; my $resty_opts = build_resty_opts(\%resty_opts); if (@ngx_rpaths) { unshift @ngx_ld_opts, "-Wl,-rpath," . join(":", @ngx_rpaths); } my $ld_opts = ''; if (@ngx_ld_opts) { $ld_opts = " \\\n --with-ld-opt='@ngx_ld_opts'"; } my $cmd = "./configure --prefix=$ngx_prefix" . $resty_opts . $ld_opts . (@ngx_opts ? " \\\n @ngx_opts" : ""); ; shell $cmd, $dry_run; push @make_cmds, "cd $root_dir/build/$ngx_dir && " . "\$(MAKE)"; push @make_install_cmds, "cd $root_dir/build/$ngx_dir && " . "\$(MAKE) install DESTDIR=\$(DESTDIR)"; cd '../..'; # to the root #die "pwd: " .. `pwd`; gen_makefile(); if ($postamble) { print $postamble; } sub env ($$) { my ($name, $val) = @_; print "export $name='$val'\n"; $ENV{$name} = $val; } sub shell ($@) { my ($cmd, $dry_run) = @_; print "$cmd\n"; unless ($dry_run) { system($cmd) == 0 or die "failed to run command: $cmd\n"; } } sub auto_complete ($) { my $name = shift; my @dirs = glob "$name-[0-9]*" or die "No source directory found for $name\n"; if (@dirs > 1) { die "More than one hits for $name: @dirs\n"; } return $dirs[0]; } sub cd ($) { my $dir = shift; print("cd $dir\n"); chdir $dir or die "failed to cd $dir: $!\n"; } sub build_resty_opts { my $opts = shift; my $make; if ($opts->{make}) { $make = $opts->{make}; if (! can_run($make)) { die "make utility $make cannot be run.\n"; } } else { if (can_run("gmake")) { $make = 'gmake'; } else { # no gmake found if ($platform =~ /bsd/i && $opts->{luajit}) { die "you need to install gmake to build LuaJIT.\n"; } if (can_run("make")) { $make = "make"; } else { die "No gmake nor make found in PATH.\n"; } } } $postamble .= <<"_END_"; Type the following commands to build and install: $make $make install _END_ if ($opts->{no_ndk}) { for my $name (qw(lua set_misc iconv lz_session form_input array_var)) { if (! $opts->{"no_http_$name"}) { warn "WARNING: ngx_http_${name}_module is automatically disabled ", "because ngx_devel_kit_module is disabled.\n"; $opts->{"no_http_$name"} = 1; } } } if (! $opts->{luajit} && ! $opts->{no_http_lua} && ! $opts->{no_lua}) { $opts->{lua} = 1; } if ($opts->{no_http_ssl} && $opts->{http_ssl}) { die "--with-http_ssl_module conflicts with --without-http_ssl_module.\n"; } if (! $opts->{no_http_ssl} && ! $opts->{http_ssl}) { $opts->{http_ssl} = 1; push @ngx_opts, '--with-http_ssl_module'; } if (! $opts->{http_drizzle} && $opts->{libdrizzle}) { die "The http_drizzle_module is not enabled while --with-libdrizzle is specified.\n"; } if (! $opts->{http_postgres} && $opts->{libpq}) { die "The http_postgres_module is not enabled while --with-libpq is specified.\n"; } if (! $opts->{http_postgres} && $opts->{pg_config}) { die "The http_postgres_module is not enabled while --with-pg_config is specified.\n"; } if ($platform eq 'linux' && $opts->{luajit} && ! can_run("ldconfig")) { die "you need to have ldconfig in your PATH env when enabling luajit.\n"; } my $opts_line = ''; if ($opts->{debug}) { unshift @ngx_cc_opts, '-O0'; $opts_line .= " \\\n --with-debug"; } else { #unshift @ngx_cc_opts, '-O2'; } if (@ngx_cc_opts) { $opts_line .= " \\\n --with-cc-opt='@ngx_cc_opts'"; } if (-d 'build') { system("rm -rf build"); } if (-f 'build') { die "build/ directory already exists\n"; } shell "cp -rp bundle/ build/"; cd 'build'; # build 3rd-party C libraries if required if ($opts->{no_pool}) { if (! can_run("patch")) { die "no \"patch\" utility found in your PATH environment.\n"; } shell "patch -p0 < nginx-no_pool.patch"; } if (my $drizzle_prefix = $opts->{libdrizzle}) { my $drizzle_lib = "$drizzle_prefix/lib"; env LIBDRIZZLE_LIB => $drizzle_lib; env LIBDRIZZLE_INC => "$drizzle_prefix/include/libdrizzle-1.0"; push @ngx_rpaths, $drizzle_lib; } if (my $pg_prefix = $opts->{libpq}) { my $pg_lib = "$pg_prefix/lib"; env LIBPQ_LIB => $pg_lib; env LIBPQ_INC => "$pg_prefix/include"; push @ngx_rpaths, $pg_lib; } if (my $pg_config = $opts->{pg_config}) { if (!can_run($pg_config)) { die "pg_config is not runnable.\n"; } my $cmd = "$pg_config --libdir"; my $pg_lib = `$cmd`; chomp $pg_lib; if (!defined $pg_lib) { die "Failed to run command $cmd\n"; } $cmd = "$pg_config --includedir"; my $pg_inc = `$cmd`; chomp $pg_inc; if (!defined $pg_inc) { die "Failed to run command $cmd\n"; } env LIBPQ_LIB => $pg_lib; env LIBPQ_INC => $pg_inc; push @ngx_rpaths, $pg_lib; } if ($opts->{luajit}) { my $luajit_src = auto_complete 'LuaJIT'; my $luajit_prefix = "$prefix/luajit"; my $luajit_root = File::Spec->rel2abs("luajit-root"); if (-d $luajit_root) { shell "rm -rf $luajit_root"; } mkdir $luajit_root or die "create create directory luajit-root: $!\n"; cd $luajit_src; my $extra_opts = ' TARGET_STRIP=@:'; if ($opts->{debug}) { $extra_opts = ' CCDEBUG=-g Q='; } if ($platform =~ /bsd/i) { $extra_opts .= ' CFLAGS=-I..'; } if ($on_solaris) { $extra_opts .= " INSTALL_X='$root_dir/build/install -m 0755' " . "INSTALL_F='$root_dir/build/install -m 0644'"; } if (defined $cc) { $extra_opts .= " CC=$cc"; } shell "${make}$extra_opts PREFIX=$luajit_prefix", $dry_run; shell "${make} install$extra_opts PREFIX=$luajit_prefix DESTDIR=$luajit_root", $dry_run; push @make_cmds, "cd $root_dir/build/$luajit_src && " . "\$(MAKE)$extra_opts PREFIX=$luajit_prefix"; push @make_install_cmds, "cd $root_dir/build/$luajit_src && " . "\$(MAKE) install$extra_opts PREFIX=$luajit_prefix DESTDIR=\$(DESTDIR)"; env LUAJIT_LIB => "$luajit_root$luajit_prefix/lib"; env LUAJIT_INC => "$luajit_root$luajit_prefix/include/luajit-2.0"; push @ngx_rpaths, "$luajit_prefix/lib"; cd '..'; } elsif ($opts->{lua}) { # build stdandard lua my $lua_src = auto_complete 'lua'; if (!defined $lua_src) { die "No lua5 found"; } my $lua_prefix = "$prefix/lua"; my $lua_root = File::Spec->rel2abs("lua-root"); if (-d $lua_root) { shell "rm -rf $lua_root"; } mkdir $lua_root or die "create create directory lua-root: $!\n"; cd $lua_src; my $extra_opts = ''; if (defined $cc) { $extra_opts .= " CC=$cc"; } shell "${make}$extra_opts $platform", $dry_run; shell "${make} install$extra_opts INSTALL_TOP=$lua_root$lua_prefix", $dry_run; env LUA_LIB => "$lua_root$lua_prefix/lib"; env LUA_INC => "$lua_root$lua_prefix/include"; push @make_cmds, "cd $root_dir/build/$lua_src && \$(MAKE)$extra_opts $platform"; push @make_install_cmds, "cd $root_dir/build/$lua_src && " . "\$(MAKE) install$extra_opts INSTALL_TOP=\$(DESTDIR)$lua_prefix"; cd '..'; } if ($opts->{lua} || $opts->{luajit}) { # build lua modules my $lualib_prefix = "$prefix/lualib"; my $ngx_lua_dir = auto_complete 'ngx_lua'; open my $in, ">>$ngx_lua_dir/config" or die "Cannot open $ngx_lua_dir/config for appending: $!\n"; print $in <<"_EOC_"; CFLAGS=\$"\$CFLAGS -DLUA_DEFAULT_PATH='\\"$lualib_prefix/?.lua\\"'" CFLAGS=\$"\$CFLAGS -DLUA_DEFAULT_CPATH='\\"$lualib_prefix/?.so\\"'" _EOC_ close $in; unless ($opts->{no_lua_cjson}) { my $dir = auto_complete 'lua-cjson'; if (!defined $dir) { die "No lua-cjson found"; } my $install; if ($on_solaris) { $install = "$root_dir/build/install"; } else { $install = "install"; } my $lua_inc = $ENV{LUA_INC}; my $extra_opts = " DESTDIR=\$(DESTDIR) LUA_INCLUDE_DIR=$lua_inc " . "LUA_LIB_DIR=$lualib_prefix"; push @make_cmds, "cd $root_dir/build/$dir && ". "\$(MAKE)$extra_opts"; push @make_install_cmds, "cd $root_dir/build/$dir && " . "\$(MAKE) install$extra_opts"; } } # prepare nginx configure line $ngx_dir = auto_complete "nginx"; cd $ngx_dir; for my $mod (@modules) { my ($name, $prefix, $attr) = @$mod; if ($attr && $attr eq 'disabled') { next if not $opts->{"$name"}; } else { next if $opts->{"no_$name"}; } my $dir = auto_complete "../$prefix"; $opts_line .= " \\\n --add-module=$dir"; } return $opts_line; } sub usage ($) { my $retval = shift; my $msg = <<'_EOC_'; --help this message --prefix=PATH set the installation prefix --with-debug enable the debugging logging and also enable -O0 -g for gcc. this not only affects nginx, but also other components. --with-no-pool-patch enable the no-pool patch for debugging memory issues. _EOC_ my $opt_max_len = length " --without-ngx_devel_kit_module "; #warn "opt max len: $opt_max_len"; my $with_resty_opts = ''; for my $mod (@modules) { my $name = $mod->[0]; if ($name =~ /^http_/) { if (@$mod == 2) { my $opt = " --without-${name}_module"; $msg .= $opt; my $n = $opt_max_len - length $opt; if ($n > 0) { $msg .= " " x $n; } else { $msg .= "\n" . (" " x $opt_max_len); } $msg .= "disable ngx_${name}_module\n"; } else { my $opt = " --with-${name}_module"; $with_resty_opts .= $opt; my $n = $opt_max_len - length $opt; if ($n > 0) { $with_resty_opts .= " " x $n; } else { $with_resty_opts .= "\n" . (" " x $opt_max_len); } $with_resty_opts .= "enable ngx_${name}_module\n"; } } } $msg .= <<'_EOC_'; --without-ngx_devel_kit_module disable ngx_devel_kit_module --without-http_ssl_module disable ngx_http_ssl_module _EOC_ $msg .= $with_resty_opts; $msg .= <<'_EOC_'; --without-lua51 disable the bundled Lua 5.1 interpreter --with-luajit enable LuaJIT 2.0 --with-libdrizzle=DIR specify the libdrizzle 1.0 (or drizzle) installation prefix --with-libpq=DIR specify the libpq (or postgresql) installation prefix --with-pg_config=PATH specify the path of the pg_config utility Options directly inherited from nginx --sbin-path=PATH set path to the nginx binary file --conf-path=PATH set path to the nginx.conf file --error-log-path=PATH set path to the error log --pid-path=PATH set path to nginx.pid file --lock-path=PATH set path to nginx.lock file --user=USER set non-privilege user for the worker processes --group=GROUP set non-privilege group for the worker processes --builddir=DIR set the build directory --with-rtsig_module enable rtsig module --with-select_module enable select module --without-select_module disable select module --with-poll_module enable poll module --without-poll_module disable poll module --with-file-aio enable file aio support --with-ipv6 enable ipv6 support --with-http_realip_module enable ngx_http_realip_module --with-http_addition_module enable ngx_http_addition_module --with-http_xslt_module enable ngx_http_xslt_module --with-http_image_filter_module enable ngx_http_image_filter_module --with-http_geoip_module enable ngx_http_geoip_module --with-http_sub_module enable ngx_http_sub_module --with-http_dav_module enable ngx_http_dav_module --with-http_flv_module enable ngx_http_flv_module --with-http_gzip_static_module enable ngx_http_gzip_static_module --with-http_random_index_module enable ngx_http_random_index_module --with-http_secure_link_module enable ngx_http_secure_link_module --with-http_degradation_module enable ngx_http_degradation_module --with-http_stub_status_module enable ngx_http_stub_status_module --without-http_charset_module disable ngx_http_charset_module --without-http_gzip_module disable ngx_http_gzip_module --without-http_ssi_module disable ngx_http_ssi_module --without-http_userid_module disable ngx_http_userid_module --without-http_access_module disable ngx_http_access_module --without-http_auth_basic_module disable ngx_http_auth_basic_module --without-http_autoindex_module disable ngx_http_autoindex_module --without-http_geo_module disable ngx_http_geo_module --without-http_map_module disable ngx_http_map_module --without-http_split_clients_module disable ngx_http_split_clients_module --without-http_referer_module disable ngx_http_referer_module --without-http_rewrite_module disable ngx_http_rewrite_module --without-http_proxy_module disable ngx_http_proxy_module --without-http_fastcgi_module disable ngx_http_fastcgi_module --without-http_uwsgi_module disable ngx_http_uwsgi_module --without-http_scgi_module disable ngx_http_scgi_module --without-http_memcached_module disable ngx_http_memcached_module --without-http_limit_zone_module disable ngx_http_limit_zone_module --without-http_limit_req_module disable ngx_http_limit_req_module --without-http_empty_gif_module disable ngx_http_empty_gif_module --without-http_browser_module disable ngx_http_browser_module --without-http_upstream_ip_hash_module disable ngx_http_upstream_ip_hash_module --with-http_perl_module enable ngx_http_perl_module --with-perl_modules_path=PATH set path to the perl modules --with-perl=PATH set path to the perl binary --http-log-path=PATH set path to the http access log --http-client-body-temp-path=PATH set path to the http client request body temporary files --http-proxy-temp-path=PATH set path to the http proxy temporary files --http-fastcgi-temp-path=PATH set path to the http fastcgi temporary files --http-uwsgi-temp-path=PATH set path to the http uwsgi temporary files --http-scgi-temp-path=PATH set path to the http scgi temporary files --without-http disable HTTP server --without-http-cache disable HTTP cache --with-mail enable POP3/IMAP4/SMTP proxy module --with-mail_ssl_module enable ngx_mail_ssl_module --without-mail_pop3_module disable ngx_mail_pop3_module --without-mail_imap_module disable ngx_mail_imap_module --without-mail_smtp_module disable ngx_mail_smtp_module --with-google_perftools_module enable ngx_google_perftools_module --with-cpp_test_module enable ngx_cpp_test_module --add-module=PATH enable an external module --with-cc=PATH set path to C compiler --with-cpp=PATH set path to C preprocessor --with-cc-opt=OPTIONS set additional options for C compiler --with-ld-opt=OPTIONS set additional options for linker --with-cpu-opt=CPU build for specified CPU, the valid values: pentium, pentiumpro, pentium3, pentium4, athlon, opteron, sparc32, sparc64, ppc64 --with-make=PATH specify the default make utility to be used --without-pcre disable PCRE library usage --with-pcre force PCRE library usage --with-pcre=DIR set path to PCRE library sources --with-pcre-opt=OPTIONS set additional options for PCRE building --with-md5=DIR set path to md5 library sources --with-md5-opt=OPTIONS set additional options for md5 building --with-md5-asm use md5 assembler sources --with-sha1=DIR set path to sha1 library sources --with-sha1-opt=OPTIONS set additional options for sha1 building --with-sha1-asm use sha1 assembler sources --with-zlib=DIR set path to zlib library sources --with-zlib-opt=OPTIONS set additional options for zlib building --with-zlib-asm=CPU use zlib assembler sources optimized for specified CPU, the valid values: pentium, pentiumpro --with-libatomic force libatomic_ops library usage --with-libatomic=DIR set path to libatomic_ops library sources --with-openssl=DIR set path to OpenSSL library sources --with-openssl-opt=OPTIONS set additional options for OpenSSL building --dry-run dry running the configure, for testing only --platform=PLATFORM forcibly specify a platform name, for testing only _EOC_ if ($retval == 0) { print $msg; exit 0; } warn $msg; exit $retval; } sub gen_makefile { open my $out, ">Makefile" or die "Cannot open Makefile for writing: $!\n"; print $out ".PHONY: all install clean\n\n"; print $out "all:\n\t" . join("\n\t", @make_cmds) . "\n\n"; print $out "install:\n\t" . join("\n\t", @make_install_cmds) . "\n\n"; print $out "clean:\n\trm -rf build\n"; close $out; } # check if we can run some command sub can_run { my ($cmd) = @_; #warn "can run: @_\n"; my $_cmd = $cmd; return $_cmd if -x $_cmd; return undef if $_cmd =~ m{[\\/]}; # FIXME: this is a hack; MSWin32 is not supported anyway my $path_sep = ':'; for my $dir ((split /$path_sep/, $ENV{PATH}), '.') { next if $dir eq ''; my $abs = File::Spec->catfile($dir, $_[0]); return $abs if -x $abs; } return undef; }