use 5.008_001;
use strict;
our $ROCKSDB_DIR = 'vendor/rocksdb';
sub new {
my ($class, %args) = @_;
%args = (get_options => { 'with-included-rocksdb' => { type => '!' } }, %args);
my $self = $class->SUPER::new(%args);
$self->check_compiler or die 'C++11 compiler required';
my $build_config = $self->parse_build_config();
my @extra_compiler_flags = (
qw(-x c++ -std=gnu++11 -I.),
qw(
-Wall
-Wno-reserved-user-defined-literal
-Wno-duplicate-decl-specifier
-Wno-parentheses
-Wno-unused
-Wno-literal-suffix
-Wno-unknown-warning-option
),
split(/\s+/, $build_config->{PLATFORM_CXXFLAGS}),
);
my @extra_linker_flags = (
qw(-lstdc++ -lrocksdb),
split(/\s+/, $build_config->{PLATFORM_LDFLAGS}),
);
if ($self->args('with-included-rocksdb') || !$self->have_rocksdb($build_config)) {
push @extra_compiler_flags, "-I$ROCKSDB_DIR/include";
push @extra_linker_flags, "-L$ROCKSDB_DIR";
}
my $ld = $self->config('ld');
if ($ld =~ s/^\s*env MACOSX_DEPLOYMENT_TARGET=[^\s]+ //) {
$self->config(ld => $ld);
}
if ($self->is_debug) {
$self->config(optimize => '-g -O0');
}
$self->extra_compiler_flags(@extra_compiler_flags);
$self->extra_linker_flags(@extra_linker_flags);
$self;
}
sub check_compiler {
my $self = shift;
require File::Temp;
print 'checking for C++11 support... ';
my ($fh, $filename) = File::Temp::tempfile(SUFFIX => '.cc', UNLINK => 1);
print $fh <<'END_SRC';
#if __cplusplus < 201103L
#error C++11 Required
#endif
END_SRC
my $cc = $self->config('cc');
my $failed = system "$cc -x c++ -std=gnu++11 $filename -c -o /dev/null >/dev/null 2>&1";
print $failed ? "no\n" : "yes\n";
!$failed;
}
sub parse_build_config {
my $self = shift;
my $file = File::Spec->catfile($ROCKSDB_DIR, 'build_config.mk');
-f $file or $self->make_build_config();
open my $fh, '<', $file or die $!;
my %config;
while (defined(my $line = <$fh>)) {
chomp $line;
my ($key, $val) = split '=', $line, 2;
$val =~ s/^\s+//;
$val =~ s/\s+$//;
$config{$key} = $val;
}
close $fh;
\%config;
}
sub make_build_config {
my $self = shift;
require Cwd;
my $script = File::Spec->catfile('build_tools', 'build_detect_platform');
-e File::Spec->catfile($ROCKSDB_DIR, $script) or $self->fetch_rocksdb();
my $cwd = Cwd::getcwd;
chdir $ROCKSDB_DIR or die $!;
local $ENV{ROCKSDB_ROOT} = Cwd::getcwd;
system '/bin/sh', $script, 'build_config.mk' and die $!;
chdir $cwd or die $!;
}
sub have_rocksdb {
my ($self, $config) = @_;
require File::Temp;
print 'checking for rocksdb/db.h... ';
my ($fh, $filename) = File::Temp::tempfile(SUFFIX => '.cc', UNLINK => 1);
print $fh <<'END_SRC';
#include <rocksdb/db.h>
int main() {
rocksdb::DB *db;
return 0;
}
END_SRC
my $cc = $self->config('cc');
my $options = "-x c++ -std=gnu++11 -lstdc++ $config->{PLATFORM_CXXFLAGS} $config->{PLATFORM_LDFLAGS}";
my $failed = system "$cc $options $filename -o /dev/null >/dev/null 2>&1";
print $failed ? "no\n" : "yes\n";
!$failed;
}
sub fetch_rocksdb {
my $self = shift;
system qw(git submodule update --init) and die $!;
}
sub compile_xs {
my ($self, $file, %args) = @_;
$self->log_verbose("$file -> $args{outfile}\n");
ExtUtils::ParseXS::process_file(
filename => $file,
prototypes => 0,
output => $args{outfile},
'C++' => 1,
hiertype => 1,
);
}
sub is_debug {
-d '.git';
}
sub ACTION_ppport_h {
require Devel::PPPort;
Devel::PPPort::WriteFile('ppport.h');
}
sub ACTION_typemap {
my $typemaps = ExtUtils::Typemaps->new;
$typemaps->merge(typemap => ExtUtils::Typemaps::Default->new);
$typemaps->write(file => 'typemap');
}
sub ACTION_build {
my $self = shift;
$self->do_system('make' => '-C', $ROCKSDB_DIR, 'librocksdb.a', 'OPT=-fPIC');
$self->SUPER::ACTION_build();
}
sub ACTION_clean {
my $self = shift;
$self->do_system('make' => '-C', $ROCKSDB_DIR, 'clean');
$self->SUPER::ACTION_clean();
}
1;
__END__