build-svn-deps-win.pl [plain text]
package Vars;
our $SEVEN_ZIP = 'C:\Program Files\7-Zip\7z.exe';
our $CMAKE = 'cmake';
our $NMAKE = 'nmake';
our $DEVENV = 'devenv.com';
our $VCUPGRADE = 'vcupgrade';
our $PYTHON = 'python';
our $HTTPD_VER = '2.4.4';
our $APR_VER = '1.4.6';
our $APU_VER = '1.5.2'; our $API_VER = '1.2.1'; our $ZLIB_VER = '1.2.8';
our $OPENSSL_VER = '1.0.1e';
our $PCRE_VER = '8.35';
our $BDB_VER = '5.3.21';
our $SQLITE_VER = '3071602';
our $SERF_VER = '1.3.6';
our $NEON_VER = '0.29.6';
our $AWK_URL = 'http://www.cs.princeton.edu/~bwk/btl.mirror/awk95.exe';
our $HTTPD_URL;
our $APR_URL;
our $APU_URL;
our $API_URL;
our $ZLIB_URL;
our $OPENSSL_URL;
our $PCRE_URL;
our $BDB_URL;
our $SQLITE_URL;
our $SERF_URL;
our $NEON_URL;
our $PROJREF_URL = 'https://downloads.redhoundsoftware.com/blog/ProjRef.py';
our $AWK_FILE;
our $HTTPD_FILE;
our $APR_FILE;
our $APU_FILE;
our $API_FILE;
our $ZLIB_FILE;
our $OPENSSL_FILE;
our $PCRE_FILE;
our $BDB_FILE;
our $SQLITE_FILE;
our $SERF_FILE;
our $NEON_FILE;
our $PROJREF_FILE;
our $TOPDIR = Cwd::cwd(); our $INSTDIR; our $BLDDIR; our $SRCDIR;
our $VS_VER;
our $NEON;
our $SVN_VER = '1.9.x';
our $DEBUG = 0;
sub remove_dots {
my $in = shift;
$in =~ tr/.//d;
return $in;
}
sub set_default {
my $var = shift;
my $value = shift;
unless (defined($$var)) {
$$var = $value;
}
}
sub set_svn_ver_defaults {
my ($svn_major, $svn_minor, $svn_patch) = $SVN_VER =~ /^(\d+)\.(\d+)\.(.+)$/;
if ($svn_major > 1 or ($svn_major == 1 and $svn_minor >= 8)) {
$NEON=0 unless defined($NEON);
} else {
$NEON=1 unless defined($NEON);
}
}
sub set_defaults {
set_default(\$HTTPD_URL, "http://archive.apache.org/dist/httpd/httpd-$HTTPD_VER.tar.bz2");
set_default(\$APR_URL, "http://archive.apache.org/dist/apr/apr-$APR_VER.tar.bz2");
set_default(\$APU_URL, "http://archive.apache.org/dist/apr/apr-util-$APU_VER.tar.bz2");
set_default(\$API_URL, "http://archive.apache.org/dist/apr/apr-iconv-$API_VER.tar.bz2");
set_default(\$ZLIB_URL, "http://sourceforge.net/projects/libpng/files/zlib/$ZLIB_VER/zlib" . remove_dots($ZLIB_VER) . '.zip');
set_default(\$OPENSSL_URL, "http://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz");
set_default(\$PCRE_URL, "ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-$PCRE_VER.zip");
set_default(\$BDB_URL, "http://download.oracle.com/berkeley-db/db-5.3.21.zip");
set_default(\$SQLITE_URL, "http://www.sqlite.org/2013/sqlite-amalgamation-$SQLITE_VER.zip");
set_default(\$SERF_URL, "https://archive.apache.org/dist/serf/serf-$SERF_VER.zip");
set_default(\$NEON_URL, "http://www.webdav.org/neon/neon-$NEON_VER.tar.gz");
set_default(\$INSTDIR, $TOPDIR);
set_default(\$BLDDIR, "$TOPDIR\\build");
set_default(\$SRCDIR, "$TOPDIR\\sources");
set_svn_ver_defaults();
}
package main;
use warnings;
use strict;
use LWP::Simple;
use File::Path;
use File::Copy;
use File::Basename;
use File::Find;
use Cwd;
use Config;
my $PERL = $Config{perlpath};
my $HTTPD; my $BDB; my $BINDIR; my $LIBDIR; my $INCDIR; my $SRCLIB;
sub set_paths {
$HTTPD = "$BLDDIR\\httpd";
$BDB = "$BLDDIR\\bdb";
$BINDIR = "$INSTDIR\\bin";
$LIBDIR = "$INSTDIR\\lib";
$INCDIR = "$INSTDIR\\include";
$SRCLIB = "$HTTPD\\srclib";
$ENV{PATH} = "$BINDIR;$ENV{PATH}";
$ENV{LIB} = "$LIBDIR;$ENV{LIB}";
$ENV{INCLUDE} = "$INCDIR;$ENV{INCLUDE}";
}
sub copy_or_die {
my $src = shift;
my $dest = shift;
copy($src, $dest) or die "Failed to copy $src to $dest: $!";
}
sub rename_or_die {
my $src = shift;
my $dest = shift;
rename($src, $dest) or die "Failed to rename $src to $dest: $!";
}
sub chdir_or_die {
my $dir = shift;
chdir($dir) or die "Failed to chdir to $dir: $!";
}
sub system_or_die {
my $error_msg = shift;
unless (system(@_) == 0) {
if (defined($error_msg)) {
die "$error_msg (exit code: $?)";
} else {
die "Failed while running '@_' (exit code: $?)";
}
}
}
sub modify_file_in_place {
my $file = shift;
my $func = shift;
my $bak = shift;
unless (defined($bak)) {
$bak = '.orig';
}
my $backup = $file . $bak;
return if -e $backup;
rename_or_die($file, $backup);
open(IN, "<$backup") or die "Failed to open $backup: $!";
open(OUT, ">$file") or die "Failed to open $file: $!";
while (<IN>) {
&{$func}();
print OUT;
}
close(IN);
close(OUT);
}
sub check_vs_ver {
return if defined($VS_VER);
my $help_output = `"$VCUPGRADE" /?`;
my ($major_version) = $help_output =~ /Version (\d+)\./s;
if (defined($major_version)) {
if ($major_version eq '12') {
$VS_VER = '2013';
return;
} elsif ($major_version eq '11') {
$VS_VER = '2012';
return;
} elsif ($major_version eq '10') {
$VS_VER = '2010';
return;
}
}
die("Visual Studio Version Not Supported");
}
sub prepare_structure {
mkdir($BINDIR);
mkdir($SRCDIR);
mkdir($BLDDIR);
mkdir($LIBDIR);
mkdir($INCDIR);
}
sub clean_structure {
my $real_clean = shift;
if ($real_clean) {
rmtree($SRCDIR);
}
rmtree($BINDIR);
rmtree($BLDDIR);
rmtree($INCDIR);
rmtree($LIBDIR);
rmtree("$INSTDIR\\serf");
rmtree("$INSTDIR\\neon");
rmtree("$INSTDIR\\sqlite-amalgamation");
rmtree("$INSTDIR\\man");
rmtree("$INSTDIR\\share");
rmtree("$INSTDIR\\ssl");
rmtree("$INSTDIR\\cgi-bin");
rmtree("$INSTDIR\\conf");
rmtree("$INSTDIR\\error");
rmtree("$INSTDIR\\htdocs");
rmtree("$INSTDIR\\icons");
rmtree("$INSTDIR\\logs");
rmtree("$INSTDIR\\manual");
rmtree("$INSTDIR\\modules");
unlink("$INSTDIR\\ABOUT_APACHE.txt");
unlink("$INSTDIR\\CHANGES.txt");
unlink("$INSTDIR\\INSTALL.txt");
unlink("$INSTDIR\\LICENSE.txt");
unlink("$INSTDIR\\NOTICE.txt");
unlink("$INSTDIR\\OPENSSL-NEWS.txt");
unlink("$INSTDIR\\OPENSSL-README.txt");
unlink("$INSTDIR\\README.txt");
}
sub download_file {
my $url = shift;
my $file = shift;
my $dest_ref = shift;
return if (defined($$dest_ref));
print "Downloading $url\n";
my $response = mirror($url, $file);
if (is_error($response)) {
die "Couldn't save $url to $file received $response";
}
$$dest_ref = $file;
}
sub download_dependencies {
download_file($AWK_URL, "$SRCDIR\\awk.exe", \$AWK_FILE);
unless(-x "$BINDIR\\awk.exe") { copy_or_die($AWK_FILE, "$BINDIR\\awk.exe");
}
download_file($PROJREF_URL, "$SRCDIR\\ProjRef.py", \$PROJREF_FILE);
unless(-x "$BINDIR\\ProjRef.py") { copy_or_die($PROJREF_FILE, $BINDIR);
}
download_file($BDB_URL, "$SRCDIR\\db.zip", \$BDB_FILE);
download_file($ZLIB_URL, "$SRCDIR\\zlib.zip", \$ZLIB_FILE);
download_file($OPENSSL_URL, "$SRCDIR\\openssl.tar.gz", \$OPENSSL_FILE);
download_file($HTTPD_URL, "$SRCDIR\\httpd.tar.bz2", \$HTTPD_FILE);
download_file($APR_URL, "$SRCDIR\\apr.tar.bz2", \$APR_FILE);
download_file($APU_URL, "$SRCDIR\\apr-util.tar.bz2", \$APU_FILE);
download_file($API_URL, "$SRCDIR\\apr-iconv.tar.bz2", \$API_FILE);
download_file($PCRE_URL, "$SRCDIR\\pcre.zip", \$PCRE_FILE);
download_file($SQLITE_URL, "$SRCDIR\\sqlite-amalgamation.zip", \$SQLITE_FILE);
download_file($SERF_URL, "$SRCDIR\\serf.zip", \$SERF_FILE);
download_file($NEON_URL, "$SRCDIR\\neon.tar.gz", \$NEON_FILE) if $NEON;
}
sub extract_file {
my $file = shift;
my $container = shift;
my $expected_name = shift;
my $rename_to = shift;
if (defined($rename_to)) {
return if -d $rename_to;
} elsif (defined($expected_name)) {
return if -d $expected_name;
}
my $dest_opt = "";
if (defined($container)) {
$dest_opt = qq(-o"$container" );
}
my $cmd;
if ($file =~ /\.tar\.(bz2|gz)$/) {
$cmd = qq("$SEVEN_ZIP" x "$file" -so | "$SEVEN_ZIP" x -y -si -ttar $dest_opt);
} else {
$cmd = qq("$SEVEN_ZIP" x -y $dest_opt $file);
}
system_or_die("Problem extracting $file", $cmd);
if (defined($rename_to)) {
rename_or_die($expected_name, $rename_to);
}
}
sub extract_dependencies {
extract_file($BDB_FILE, $BLDDIR,
"$BLDDIR\\db-$BDB_VER", "$BLDDIR\\bdb");
extract_file($HTTPD_FILE, $BLDDIR,
"$BLDDIR\\httpd-$HTTPD_VER", "$BLDDIR\\httpd");
extract_file($APR_FILE, $SRCLIB,
"$SRCLIB\\apr-$APR_VER", "$SRCLIB\\apr");
extract_file($APU_FILE, $SRCLIB,
"$SRCLIB\\apr-util-$APU_VER", "$SRCLIB\\apr-util");
extract_file($API_FILE, $SRCLIB,
"$SRCLIB\\apr-iconv-$API_VER", "$SRCLIB\\apr-iconv");
httpd_fix_lineends();
extract_file($ZLIB_FILE, $SRCLIB,
"$SRCLIB\\zlib-$ZLIB_VER", "$SRCLIB\\zlib");
extract_file($OPENSSL_FILE, $SRCLIB,
"$SRCLIB\\openssl-$OPENSSL_VER", "$SRCLIB\\openssl");
extract_file($PCRE_FILE, $SRCLIB,
"$SRCLIB\\pcre-$PCRE_VER", "$SRCLIB\\pcre");
extract_file($SQLITE_FILE, $INSTDIR,
"$INSTDIR\\sqlite-amalgamation-$SQLITE_VER",
"$INSTDIR\\sqlite-amalgamation");
extract_file($SERF_FILE, $INSTDIR,
"$INSTDIR\\serf-$SERF_VER", "$INSTDIR\\serf");
extract_file($NEON_FILE, $INSTDIR,
"$INSTDIR\\neon-$NEON_VER", "$INSTDIR\\neon") if $NEON;
}
sub build_pcre {
chdir_or_die("$SRCLIB\\pcre");
my $pcre_generator = 'NMake Makefiles';
my $pcre_build_type = '-DCMAKE_BUILD_TYPE:STRING=' . ($DEBUG ? 'Debug' : 'RelWithDebInfo');
my $pcre_options = '-DPCRE_NO_RECURSE:BOOL=ON';
my $pcre_shared_libs = '-DBUILD_SHARED_LIBS:BOOL=ON';
my $pcre_install_prefix = "-DCMAKE_INSTALL_PREFIX:PATH=$INSTDIR";
my $cmake_cmd = qq("$CMAKE" -G "$pcre_generator" "$pcre_build_type" "$pcre_shared_libs" "$pcre_install_prefix" "$pcre_options" .);
system_or_die("Failure generating pcre Makefiles", $cmake_cmd);
system_or_die("Failure building pcre", qq("$NMAKE"));
system_or_die("Failure testing pcre", qq("$NMAKE" test));
system_or_die("Failure installing pcre", qq("$NMAKE" install));
chdir_or_die($TOPDIR);
}
sub build_zlib {
chdir_or_die("$SRCLIB\\zlib");
$ENV{CC_OPTS} = $DEBUG ? '/MDd /Gm /ZI /Od /GZ /D_DEBUG' : '/MD /02 /Zi';
$ENV{COMMON_CC_OPTS} = '/nologo /W3 /DWIN32 /D_WINDOWS';
system_or_die("Failure building zilb", qq("$NMAKE" /nologo -f win32\\Makefile.msc STATICLIB=zlibstat.lib all));
delete $ENV{CC_OPTS};
delete $ENV{COMMON_CC_OPTS};
chdir_or_die($TOPDIR);
}
sub build_openssl {
chdir_or_die("$SRCLIB\\openssl");
my $platform = $DEBUG ? 'debug-VC-WIN32' : 'VC-WIN32';
system_or_die("Failure configuring openssl",
qq("$PERL" Configure no-asm "--prefix=$INSTDIR" $platform));
system_or_die("Failure building openssl (bat)", 'ms\do_ms.bat');
system_or_die("Failure building openssl (nmake)", qq("$NMAKE" /f ms\\ntdll.mak));
system_or_die("Failure testing openssl", qq("$NMAKE" /f ms\\ntdll.mak test));
system_or_die("Failure installing openssl",
qq("$NMAKE" /f ms\\ntdll.mak install));
chdir_or_die($TOPDIR);
}
sub upgrade_solution {
my $file = shift;
my $interactive = shift;
my $flags = "";
my ($basename, $directories) = fileparse($file, qr/\.[^.]*$/);
my $sln = $directories . $basename . '.sln';
return if $file ne $sln and -s $sln; unless (-e $sln) {
open(SLN, ">$sln") or die "Can't create $sln: $!";
close(SLN);
}
print "Upgrading $file (this may take a while)\n";
$flags = " /Upgrade" unless $interactive;
system_or_die("Failure upgrading $file", qq("$DEVENV" "$file"$flags));
if ($interactive) {
print "Can't do automatic upgrade, doing interactive upgrade\n";
print "IDE will load, choose to convert all projects, exit the IDE and\n";
print "save the resulting solution file\n\n";
print "Press Enter to Continue\n";
<>;
}
}
sub httpd_fix_lineends {
chdir_or_die($HTTPD);
system_or_die(undef, qq("$PERL" "$SRCLIB\\apr\\build\\lineends.pl"));
chdir_or_die($TOPDIR);
}
sub httpd_fix_makefile {
my $file = shift;
modify_file_in_place($file, sub {
s/\.vcproj/.vcxproj/i;
s/^(\s*copy srclib\\pcre\\pcre\.\$\(src_dll\)\s+"\$\(inst_dll\)"\s+<\s*\.y\s*)$/!IF EXISTS("srclib\\pcre\\pcre\.\$(src_dll)")\n$1!ENDIF\n!IF EXISTS("srclib\\pcre\\pcred\.\$(src_dll)")\n\tcopy srclib\\pcre\\pcred.\$(src_dll)\t\t\t"\$(inst_dll)" <.y\n!ENDIF\n/;
});
}
sub insert_property_group {
my $file = shift;
my $xml = shift;
my $bak = shift;
modify_file_in_place($file, sub {
s }, $bak);
}
sub disable_pch {
my $file = shift;
modify_file_in_place($file, sub {
s });
}
sub get_output_file {
my $file = shift;
my $result;
local $_;
open(IN, "<$file") or die "Couldn't open file $file: $!";
while (<IN>) {
if (m $result = $1;
last;
}
}
close(IN);
return $result;
}
sub find_bdb_lib {
my $result;
my $debug = $DEBUG ? 'd' : '';
find(sub {
if (not defined($result) and /^libdb\d+$debug\.lib$/) {
$result = $_;
}
}, $LIBDIR);
return $result;
}
sub insert_dependency_in_proj {
my $file = shift;
my $dep = shift;
my $bak = shift;
modify_file_in_place($file, sub {
s/(%\(AdditionalDependencies\))/$dep;$1/;
}, $bak);
}
sub httpd_enable_bdb {
modify_file_in_place('srclib\apr-util\include\apu.hw', sub {
s/( });
my $bdb_lib = find_bdb_lib();
modify_file_in_place('srclib\apr-util\dbm\apr_dbm_db.vcxproj', sub {
s/libdb\d+\.lib/$bdb_lib/g;
}, '.bdb');
insert_dependency_in_proj('support\httxt2dbm.vcxproj', $bdb_lib, '.bdb');
insert_dependency_in_proj('support\htdbm.vcxproj', $bdb_lib, '.bdb');
}
sub httpd_fix_debug {
my ($httpd_major, $httpd_minor, $httpd_patch) = $HTTPD_VER =~ /^(\d+)\.(\d+)\.(.+)$/;
return unless ($httpd_major <= 2 && $httpd_minor <= 4 && $httpd_patch < 5);
modify_file_in_place('libhttpd.dsp', sub {
s/^(!MESSAGE "libhttpd - Win32 Debug" \(based on "Win32 \(x86\) Dynamic-Link Library"\))$/$1\n!MESSAGE "libhttpd - Win32 Lexical" (based on "Win32 (x86) Dynamic-Link Library")/;
s/^( }, '.lexical');
}
sub build_httpd {
chdir_or_die($HTTPD);
my $vs_2013 = $VS_VER eq '2013';
my $vs_2012 = $VS_VER eq '2012';
my $vs_2010 = $VS_VER eq '2010';
httpd_fix_debug();
system_or_die("Failure converting DSP files",
qq("$PERL" srclib\\apr\\build\\cvtdsp.pl -2005));
upgrade_solution('Apache.dsw', $vs_2010);
httpd_enable_bdb();
httpd_fix_makefile('Makefile.win');
find(sub {
if (/\.vcxproj$/) {
insert_property_group($_, '<TrackFileAccess>false</TrackFileAccess>')
}
}, 'modules', 'support');
if ($vs_2012 or $vs_2013) {
disable_pch('srclib\apr-iconv\build\modules.mk.win');
insert_property_group('support\win32\ApacheMonitor.vcxproj',
'<GenerateManifest>false</GenerateManifest>',
'.dupman');
find(sub {
return unless (/\.vcxproj$/);
my $output_file = get_output_file($_);
return unless (defined($output_file));
my ($project_name) = fileparse($_, qr/\.[^.]*$/);
my ($old_style_target_name) = fileparse($output_file, qr/\.[^.]*$/);
return if ($old_style_target_name eq $project_name);
insert_property_group($_,
"<TargetName>$old_style_target_name</TargetName>", '.torig');
}, "$SRCLIB\\apr", "$SRCLIB\\apr-util", "$SRCLIB\\apr-iconv");
} elsif ($vs_2010) {
system_or_die("Failed fixing project guid references",
qq("$PYTHON" "$BINDIR\\ProjRef.py" -i Apache.sln"));
}
# If you're looking here it's possible that something went
# wrong with the httpd build. Debugging it can be a bit of a pain
# when using this script. There are log files created in the
# Release dirs named with the same basename as the project. E.G.
# for support\httxt2dbm.vcxproj you can find the log in
# support\Release\httxt2dbm.log. You can also run a similar build
# from in the IDE, but you'll need to disable some projects since
# they are separately driven by the Makefile.win. Grepping for
# '/project' in Makefile.win should tell you which projects. You'll
# also need to add the bin, include and lib paths to the appropriate
# configurations inside the project since we get them from the environment.
# Once all that is done the BuildBin project should be buildable for you to
# diagnose the problem.
my $target = $DEBUG ? "installd" : "installr";
system_or_die("Failed building/installing httpd/apr/apu/api",
qq("$NMAKE" /f Makefile.win $target "DBM_LIST=db" "INSTDIR=$INSTDIR"));
chdir_or_die($TOPDIR);
}
sub build_bdb {
chdir_or_die($BDB);
print(cwd(),$/);
my $sln = 'build_windows\Berkeley_DB_vs2010.sln';
upgrade_solution($sln);
my $platform = $DEBUG ? 'Debug|Win32' : 'Release|Win32';
# Build the db Project first since the full solution fails due to a broken
# dependency with the current version of BDB if we don't.
system_or_die("Failed building DBD (Project db)",
qq("$DEVENV" "$sln" /Build "$platform" /Project db));
system_or_die("Failed building DBD",
qq("$DEVENV" "$sln" /Build "$platform"));
# BDB doesn't seem to have it's own install routines so we'll do it ourselves
copy_or_die('build_windows\db.h', $INCDIR);
find(sub {
if (/\.(exe|dll|pdb)$/) {
copy_or_die($_, $BINDIR);
} elsif (/\.lib$/) {
copy_or_die($_, $LIBDIR);
}
}, 'build_windows\\Win32\\' . ($DEBUG ? 'Debug' : 'Release'));
chdir_or_die($TOPDIR);
}
# Right now this doesn't actually build serf but just patches it so that it
# can build against a debug build of OpenSSL.
sub build_serf {
chdir_or_die("$TOPDIR\\serf");
modify_file_in_place('serf.mak', sub {
s/^(INTDIR = Release)$/$1\nOPENSSL_OUT_SUFFIX =/;
s/^(INTDIR = Debug)$/$1\nOPENSSL_OUT_SUFFIX = .dbg/;
s/(\$\(OPENSSL_SRC\)\\out32(?:dll)?)/$1\$(OPENSSL_OUT_SUFFIX)/g;
}, '.debug');
chdir_or_die($TOPDIR);
}
sub build_dependencies {
build_bdb();
build_zlib();
build_pcre();
build_openssl();
build_serf();
build_httpd();
}
###############
# COMMANDLINE #
###############
# Implement an interface somewhat similar to the make command line
# You can give a list of commands and variable assignments interspersed.
# Variable assignments are always VAR=VALUE with no spaces (in a single
# argv entry).
sub main {
my @commands;
while (my $arg = shift @ARGV) {
# Look for variable assignment
if (my ($lhs, $rhs) = $arg =~ /([^=]+)=(.*)/) {
# Bit of hackery to allow the global values in the
# Vars package to be overriden from the command line.
# E.G. "CMAKE=C:\CMake\cmake.exe" would replace the
# default value with this value.
if (exists($Vars::{$lhs})) {
${$Vars::{$lhs}} = $rhs;
} else {
# Don't allow variables that don't exist already to be touched.
die "$lhs is an unknown variable.";
}
} else {
# Not a variable so must be a command
push @commands, $arg;
}
}
# No commands so add the implicit all command
if ($#commands == -1) {
push @commands, 'all';
}
# Set defaults and paths that have to be set at runtime since they are based
# on other variables.
Vars::set_defaults();
set_paths();
# Determine the Visual Studio Version and die if not supported.
check_vs_ver();
# change directory to our TOPDIR before running any commands
# the variable assignment might have changed it.
chdir_or_die($TOPDIR);
# Run the commands in the order given.
foreach my $command (@commands) {
if ($command eq 'clean') {
clean_structure(0);
} elsif ($command eq 'real-clean') {
clean_structure(1);
} elsif ($command eq 'prepare') {
prepare_structure();
} elsif ($command eq 'download') {
download_dependencies();
} elsif ($command eq 'extract') {
extract_dependencies();
} elsif ($command eq 'all') {
prepare_structure();
download_dependencies();
extract_dependencies();
build_dependencies();
} else {
die "Command '$command' is unknown";
}
}
}
main();