mysql5/mysql-5.7.27/storage/ndb/compile-cluster

790 lines
19 KiB
Perl
Executable File

#!/usr/bin/perl
# Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# -*- cperl -*-
#
# MySQL Cluster compile script to bridge the gap between
# different build systems in different versions of MySQL Server
#
# This script is intended for internal use
#
use strict;
use Cwd 'abs_path';
use File::Basename;
use Getopt::Long;
# Automatically flush STDOUT
select(STDOUT);
$| = 1;
# Only add the command line options handled by this script,
# thus acting like a filter and passing all other arguments
# straight through
my $opt_debug;
my $opt_build_type;
my $opt_build = 1;
my $opt_just_print;
my $opt_vanilla;
my $opt_autotest;
my $opt_valgrind;
my $opt_distcheck;
my $opt_parse_log;
Getopt::Long::Configure("pass_through");
GetOptions(
# Build MySQL Server and NDB with debug
'debug!' => \$opt_debug,
'with-debug:s' => sub { $opt_debug = 1; },
'build-type=s' => \$opt_build_type,
'build!' => \$opt_build,
'c|just-configure' => sub { $opt_build = 0; },
'n|just-print' => \$opt_just_print,
'vanilla' => \$opt_vanilla,
'autotest' => \$opt_autotest,
'valgrind' => \$opt_valgrind,
'distcheck' => \$opt_distcheck,
# Special switch --parse-log=<file> which reads a log file (from build) and
# parses it for warnings
'parse-log=s' => \$opt_parse_log,
) or exit(1);
# Find source root directory, assume this script is
# in <srcroot>/storage/ndb/
my $opt_srcdir = dirname(dirname(dirname(abs_path($0))));
die unless -d $opt_srcdir; # Sanity check that the srcdir exist
if ($^O eq "cygwin") {
# Convert posix path to Windows mixed path since cmake
# is most likely a windows binary
$opt_srcdir= `cygpath -m $opt_srcdir`;
chomp $opt_srcdir;
}
# Parse given log file for warnings
if ($opt_parse_log)
{
use IO::File;
my $file = IO::File->new($opt_parse_log, 'r')
or die "Failed to open file $opt_parse_log: $!";
my $parser = WarningParser->new(srcdir => $opt_srcdir,
unified => 1,
verbose => 1);
while (my $line = <$file>)
{
$parser->parse_line($line);
}
$parser->report($0);
exit(0);
}
# Check that cmake exists and figure out it's version
my $cmake_version_id;
{
my $version_text = `cmake --version`;
print $version_text;
die "Could not find cmake" if ($?);
if ( $version_text =~ /^cmake version ([0-9]*)\.([0-9]*)\.*([^\s]*)/ )
{
#print "1: $1 2: $2 3: $3\n";
$cmake_version_id= $1*10000 + $2*100 + $3;
#print "cmake_version_id: $cmake_version_id\n";
}
die "Could not parse cmake version" unless ($cmake_version_id);
}
# Replace gcc with g++ in CXX environment variable
if(defined $ENV{"CXX"} and $ENV{"CXX"} =~ m/gcc/)
{
my $old_cxx= $ENV{"CXX"};
$ENV{"CXX"} =~ s/gcc/g++/;
print("compile-cluster: switched CXX compiler from '$old_cxx' to '$ENV{CXX}'\n");
}
# Remove -fno-exceptions from CXXFLAGS environment variable
if(defined $ENV{"CXXFLAGS"} and $ENV{"CXXFLAGS"} =~ "-fno-exceptions")
{
$ENV{"CXXFLAGS"} =~ s/-fno-exceptions//g;
print("compile-cluster: stripped off -fno-exceptions from CXXFLAGS='$ENV{CXXFLAGS}'\n");
}
#
# Configure
#
{
# Remove old CMakeCache.txt(ignore if not exists) to
# force fresh configure
unlink("CMakeCache.txt");
my @args;
if ($opt_debug)
{
print("compile-cluster: debug build requested\n");
push(@args, "-DWITH_DEBUG=1");
push(@args, "-DMYSQL_MAINTAINER_MODE=0");
}
if ($opt_vanilla)
{
# Use default options for building
print("compile-cluster: vanilla build requested, no sugar\n");
# Turn off automatic detection of WITH_NDBCLUSTER
# in MySQL Cluster version(i.e when version string ends in -ndb-Y.Y.Y)
push(@args, "-DWITH_NDB_DEFAULT_PLUGIN_DETECT=0");
}
else
{
# Hardcoded options controlling how to build MySQL Server
push(@args, "-DWITH_SSL=bundled"); # Consistent error messages
# Hardcoded options controlling how to build NDB
push(@args, "-DWITH_PLUGIN_NDBCLUSTER=1");
push(@args, "-DWITH_NDB_TEST=1");
push(@args, "-DWITH_NDBAPI_EXAMPLES=1")
unless ($^O eq "cygwin" or $^O eq "MSWin32")
}
if ($opt_autotest)
{
print("compile-cluster: autotest build requested, extra everything\n");
push(@args, "-DWITH_NDB_CCFLAGS='-DERROR_INSERT'");
push(@args, "-DWITH_EMBEDDED_SERVER=1");
}
if ($opt_valgrind)
{
print("compile-cluster: valgrind build requested, adjusting the knobs\n");
# Add HAVE_purify to compiler flags in order to silence
# inspected warnings
push(@args, "-DCMAKE_C_FLAGS='-DHAVE_purify'");
push(@args, "-DCMAKE_CXX_FLAGS='-DHAVE_purify'");
# Turn on use of the valgrind headers to enhance detection
push(@args, "-DWITH_VALGRIND=1");
}
# The cmake generator to use
if ($opt_build_type)
{
push(@args, "-G \"$opt_build_type\"");
}
# Sets installation directory, bindir, libdir, libexecdir etc.
# The equivalent CMake variables are given without prefix
# e.g if --prefix is /usr and --bindir is /usr/bin
# then cmake variable (INSTALL_BINDIR) must be just "bin"
my $opt_prefix;
sub set_installdir
{
my($path, $varname) = @_;
my $prefix_length = length($opt_prefix);
if (($prefix_length > 0) && (index($path,$opt_prefix) == 0))
{
# path is under the prefix, remove the prefix and
# maybe following "/"
$path = substr($path, $prefix_length);
if(length($path) > 0)
{
my $char = substr($path, 0, 1);
if($char eq "/")
{
$path= substr($path, 1);
}
}
if(length($path) > 0)
{
push(@args, "-D$varname=$path");
}
}
}
# Process --configure style arguments which need special conversion
my $opt_bindir;
my $opt_libdir;
my $opt_libexecdir;
my $opt_includedir;
my $opt_with_zlib_dir;
my $opt_with_ssl;
my $opt_localstatedir;
my $opt_mysql_maintainer_mode;
my $opt_with_gcov;
my $opt_with_comment;
my $opt_with_plugins;
my $opt_without_plugin;
my $opt_extra_charsets;
my $opt_with_extra_charsets;
Getopt::Long::Configure("pass_through");
GetOptions(
'prefix=s' => \$opt_prefix,
'srcdir=s' => \$opt_srcdir,
'bindir=s' => \$opt_bindir,
'libdir=s' => \$opt_libdir,
'libexecdir=s' => \$opt_libexecdir,
'includedir=s' => \$opt_includedir,
'with-zlib-dir=s' => \$opt_with_zlib_dir,
'with-ssl:s' => \$opt_with_ssl,
'localstatedir=s' => \$opt_localstatedir,
'mysql-maintainer-mode=s' => \$opt_mysql_maintainer_mode,
'with-gcov' => \$opt_with_gcov,
'with-comment=s' => \$opt_with_comment,
'with-plugins=s' => \$opt_with_plugins,
'without-plugin=s' => \$opt_without_plugin,
'with-extra-charsets=s' => \$opt_with_extra_charsets,
'extra-charsets=s' => \$opt_extra_charsets,
) or exit(1);
if($opt_prefix)
{
push(@args, "-DCMAKE_INSTALL_PREFIX=$opt_prefix");
}
if($opt_bindir)
{
set_installdir($opt_bindir, "INSTALL_BINDIR");
}
if($opt_libdir)
{
set_installdir($opt_libdir, "INSTALL_LIBDIR");
}
if($opt_libexecdir)
{
set_installdir($opt_libexecdir, "INSTALL_SBINDIR");
}
if($opt_includedir)
{
set_installdir($opt_includedir, "INSTALL_INCLUDEDIR");
}
if($opt_with_zlib_dir)
{
$opt_with_zlib_dir = "system"
if ($opt_with_zlib_dir ne "bundled");
push(@args, "-DWITH_ZLIB=$opt_with_zlib_dir");
}
if($opt_with_ssl)
{
push(@args, "-DWITH_SSL=".($opt_with_ssl ? "yes" : "bundled"));
}
if ($opt_localstatedir)
{
push(@args, "-DMYSQL_DATADIR=$opt_localstatedir");
}
if ($opt_mysql_maintainer_mode)
{
push(@args, "-DMYSQL_MAINTAINER_MODE=" .
($opt_mysql_maintainer_mode =~ /enable/ ? "1" : "0"));
}
if ($opt_with_gcov)
{
push(@args, "-DENABLE_GCOV=ON");
}
if ($opt_with_comment)
{
push(@args, "\"-DWITH_COMMENT=$opt_with_comment\"");
}
if($opt_with_plugins)
{
my @plugins= split(/,/, $opt_with_plugins);
foreach my $p (@plugins)
{
$p =~ s/-/_/g;
push(@args, "-DWITH_".uc($p)."=1");
}
}
if($opt_without_plugin)
{
push(@args, "-DWITHOUT_".uc($opt_without_plugin)."=1");
}
if ($opt_extra_charsets)
{
push(@args, "-DWITH_CHARSETS=$opt_extra_charsets");
}
if($opt_with_extra_charsets)
{
push(@args, "-DWITH_EXTRA_CHARSETS=$opt_with_extra_charsets");
}
# Default conversion of remaining args in ARGV from
# 1) --arg -> -DARG=1
# 2) --arg=value -> -DARG=value
# 3) arg=value -> environment variable arg=value
foreach my $option (@ARGV)
{
if ($option =~ /^--/)
{
# Remove leading --
$option = substr($option, 2);
my @v = split('=', $option);
my $name = shift(@v);
$name = uc($name);
$name =~ s/-/_/g;
if (@v)
{
push(@args, "-D$name=".join('=', @v));
}
else
{
push(@args, "-D$name=1");
}
}
else
{
# This must be environment variable
my @v = split('=', $option);
my $name = shift(@v);
if(@v)
{
$ENV{$name} = join('=', @v);
}
else
{
die "unhandled argument '$option' found";
}
}
}
# The source directory to build from
die "srcdir already contains CMakeCache.txt, this will not work!"
if (-f "$opt_srcdir/CMakeCache.txt");
push(@args, $opt_srcdir);
cmd("cmake", @args);
}
#
# Build
#
if (!$opt_build)
{
print "Configuration completed, skipping build(used --no-build)\n";
exit(0);
}
{
if ($cmake_version_id >= 20800)
{
# Use the universal "cmake --build <dir>" way of building
# which is available from cmake 2.8 and works on all platforms
my @args;
push(@args, "--build");
push(@args, ".");
if ($^O eq "cygwin" or $^O eq "MSWin32")
{
# Choose to build RelWitDebInfo by default on Windows
my $config = 'RelWithDebInfo';
if ($opt_debug)
{
$config = 'Debug';
}
push(@args, "--config");
push(@args, $config);
}
build_cmd("cmake", @args);
}
else
{
# Use make to build, not supported on Windows
die "You need to install cmake with version > 2.8"
if ($^O eq "cygwin" or $^O eq "MSWin32");
build_cmd("make");
}
}
if ($opt_distcheck)
{
print "\n";
print "NOTE! 'make distcheck' not (yet) supported in this version\n";
print "\n";
}
exit(0);
sub cmd {
my ($cmd, @a)= @_;
my $cmd_str = join(' ', $cmd, @a);
print "compile-cluster: '$cmd_str'\n";
return if ($opt_just_print);
system($cmd, @a)
and print("command '$cmd_str' failed\n")
and exit(1);
}
use IPC::Open2;
sub build_cmd {
my ($cmd, @args) = @_;
my $cmd_str = join(' ', $cmd, @args);
print "compile-cluster: '$cmd_str'\n";
return if ($opt_just_print);
$cmd_str.= " 2>&1";
# Create warning parser and pass every ouput line through it
my $parser = WarningParser->new(srcdir => $opt_srcdir,
unified => 1,
verbose => 1);
my ($chld_out, $chld_in);
my $pid = open2($chld_out, $chld_in, $cmd_str) or die $!;
# Install handler to make sure that build is killed
# off even in case the perl script dies
local $SIG{__DIE__} = sub {
print STDERR "Ooops, script died! Killing the build(pid = $pid)\n";
# NOTE! Kill with negative signal number means kill process group
my $ret = kill(-9, $pid);
print STDERR " kill(-9, $pid) -> $ret\n";
# Just checking, should return 0
$ret = kill(0, $pid);
print STDERR " kill(0, $pid) -> $ret\n";
# Wait for process to terminate
print STDERR " waitpid($pid, 0)\n";
$ret= waitpid($pid, 0);
print STDERR " waitpid returned $ret!\n";
};
while (my $line = <$chld_out>)
{
if (!$parser->parse_line($line))
{
# Warning parser didn't print the line, print it
print $line;
}
}
waitpid($pid, 0);
my $exit_status = $?;
my $exit_code = ($exit_status >> 8);
print "Build completed with exit_code: $exit_code(status: $exit_status)\n";
if ($exit_code)
{
print("command '$cmd_str' failed: $!\n");
exit(1);
}
$parser->report($0);
}
# Perl class used by WarningParser for keeping
# track of one individual warning
#
package WarningParser::Warning;
use strict;
sub new {
my ($class, $file, $line, $text, $compiler)= @_;
my $self= bless {
FILE => $file,
LINE => $line,
TEXT => $text,
COMPILER => $compiler,
}, $class;
return $self;
}
sub file {
my ($self) = @_;
return $self->{FILE};
}
sub line {
my ($self) = @_;
return $self->{LINE};
}
sub text {
my ($self) = @_;
return $self->{TEXT};
}
# Print the warning in verbose format for easier debugging
sub print_verbose {
my ($self) = @_;
print "{\n";
foreach my $key (keys %$self)
{
print " $key => '$self->{$key}'\n";
}
print "}\n";
}
# Print the warning in unified format(easy for automated build system to parse)
# emulate gcc
sub print_unified {
my ($self) = @_;
my $file = $self->file();
my $line = $self->line();
my $text = $self->text();
print "$file:$line: warning: $text\n";
}
sub suppress {
my ($self, $message) = @_;
die if exists $self->{SUPPRESSED}; # Already suppressed
die unless $message; # No message
$self->{SUPPRESSED} = $message;
}
sub is_suppressed {
my ($self) = @_;
return exists $self->{SUPPRESSED};
}
sub is_cluster_warning {
my ($self) = @_;
my $file = $self->{FILE};
# Have the string ndb in the file name(including
# directory so everything below storage/ndb is
# automatically included)
if ($file =~ /ndb/)
{
return 1;
}
return 0;
}
package WarningParser;
use strict;
use Cwd 'abs_path';
sub new {
my $class= shift;
my %opts= ( @_ );
my $srcdir = $opts{srcdir} || die "Must supply srcdir";
my $verbose = $opts{verbose} || 0;
my $unified = $opts{unified} || 0;
my $track_dirs = $opts{track_dirs} || 0;
my $self= bless {
# empty array of warnings
WARNINGS => [],
# print each warning object as they are accumulated
VERBOSE => $verbose,
# print warnings in unified format(i.e the format
# is converted to look like standard gcc). This makes it
# easy for higher level tools to parse the warnings
# regardless of compiler.
UNIFIED => $unified,
# Need to keep track of current dir since file name in
# warnings does not include directory(this is normal
# in makefiles generated by automake)
TRACK_DIRS => $track_dirs,
# Location of source
SRCDIR => $srcdir,
}, $class;
return $self;
}
sub new_warning {
my ($self, $file, $line, $text, $compiler) = @_;
#print "new_warning>\n";
#print "file: '$file', line: $line, text: '$text', compiler: '$compiler'\n";
my $srcdir = $self->{SRCDIR};
#print "srcdir: $srcdir\n";
if ($self->{TRACK_DIRS})
{
# "file" does not contain directory, add currently
# tracked dir
my $dir = $self->{DIR};
$file= "$dir/$file";
}
if (! -e $file)
{
# safety, "file" does not always contain full path
# and thus calling 'abs_path' on a non existing file
# would make the script die
print "Hmmpf, creating warning for file without full path!\n";
}
else
{
# "srcdir" is in abs_path form, convert also "file" to abs_path
$file = abs_path($file);
}
$file =~ s/^$srcdir//; # Remove leading srcdir
$file =~ s:^\/::; # Remove leading slash
return WarningParser::Warning->new($file, $line, $text, $compiler);
}
sub parse_warning {
my ($self, $line) = @_;
if ($self->{TRACK_DIRS})
{
# Track current directory by parsing makes
# "Entering/Leaving directory" messages
if ($line =~ /Entering directory \`(.*)\'/)
{
my $dir= $1;
# Push previous dir onto stack before setting new
push(@{$self->{DIRSTACK}}, $self->{DIR});
$self->{DIR}= $dir;
}
if ($line =~ /Leaving directory \`(.*)\'/)
{
# Pop previous dir from stack and set it as current
my $prevdir= pop(@{$self->{DIRSTACK}});
$self->{DIR}= $prevdir;
}
}
# cmake and Visual Studio 10(seems to use msbuild)
if ($line =~ /^(\d+>)?\s*(.*)\((\d+)\): warning ([^ ]*:.*)$/)
{
return $self->new_warning($2, $3, $4, "vs10_msbuild");
}
# cmake and Visual Studio 9
if ($line =~ /^(\d+>)?(?:[a-z]:)?([^:()]*)\((\d+)\) : warning ([^ ]*:.*)$/)
{
my ($project, $file, $lineno, $text) = ($1, $2, $3, $4);
return $self->new_warning($file, $lineno, $text, "vs9");
}
# cmake and gcc with line number AND column
if ($line =~ /([^ ]+\.(c|h|cc|cpp|hpp|ic|i|y|l)):([0-9]+):([0-9]+):[ \t]*warning:[ \t]*(.*)$/)
{
my ($file, $junk, $lineno, $colno, $text) = ($1, $2, $3, $4, $5);
return $self->new_warning($file, $lineno, $text, , "gcc_with_col");
}
# cmake and gcc
if ($line =~ /([^ ]+\.(c|h|cc|cpp|hpp|ic|i|y|l)):[ \t]*([0-9]+):[ \t]*warning:[ \t]*(.*)$/)
{
return $self->new_warning($1, $3, $4, "gcc");
}
return undef;
}
sub suppress_warning {
my ($self, $w) = @_;
# Ignore files not owned by cluster team
if (!$w->is_cluster_warning())
{
$w->suppress('Warning in file not owned by cluster team');
return 1;
}
if ($w->file() =~ /memcache\/extra/)
{
$w->suppress('Warning in imported memcache code');
return 1;
}
# List of supressions consisting of one regex for the dir+file name
# and one for the warning text. The suppression is stored as a
# list of arrays, where each array contains two precompiled
# regexes. If both expressions match, the warning is suppressed.
my @suppressions = (
# [ qr/<dirname+filename regex>/, qr/<warning regex>/ ],
[ qr/DbtuxMeta.cpp/, qr/Warray-bounds/ ],
);
foreach my $sup ( @suppressions )
{
my $file_pat = $sup->[0];
my $text_pat = $sup->[1];
if ($w->file() =~ /$file_pat/ and
$w->text() =~ /$text_pat/)
{
$w->suppress("Suppressed by file suppression: '$file_pat, $text_pat'");
return 1;
}
}
return 0;
}
# Parse a line for warnings and return 1 if warning was
# found(even if it was suppressed)
#
sub parse_line {
my ($self, $line) = @_;
$self->{LINES}++;
# Remove trailing line feed and new line
$line =~ s/[\r]+$//g;
$line =~ s/[\n]+$//g;
my $w = $self->parse_warning($line);
if (defined $w)
{
if (!$self->suppress_warning($w))
{
if ($self->{UNIFIED})
{
# Print the warning in UNIFIED format
$w->print_unified();
}
else
{
# Just echo the line verbatim
print "\n$line\n";
}
}
# Print the warning object in verbose mode
$w->print_verbose() if $self->{VERBOSE};
# Save the warning for final report
push(@{$self->{WARNINGS}}, $w);
return 1;
}
return 0;
}
sub report {
my ($self, $prefix) = @_;
my $lines = $self->{LINES};
my $warnings = 0;
my $suppressed= 0;
foreach my $w (@{$self->{WARNINGS}})
{
if ($w->is_suppressed())
{
$suppressed++;
}
else
{
$warnings++;
}
}
my $total = $warnings + $suppressed;
print "$prefix: $warnings warnings found(suppressed $suppressed of total $total)\n";
}
1;