1
0
Fork 0
mirror of https://github.com/pavel-odintsov/fastnetmon synced 2024-06-08 20:56:07 +02:00

Added new unified dependency build tool. Upgraded multiple libraries and improved CMakelist logic

This commit is contained in:
Pavel Odintsov 2022-12-02 13:34:29 +00:00
parent 3b738ead1f
commit ba4ef45099
3 changed files with 1484 additions and 7 deletions

View File

@ -17,15 +17,16 @@ set (FASTNETMON_VERSION_MAJOR 1)
set (FASTNETMON_VERSION_MINOR 2)
set (FASTNETMON_VERSION_PATCH 4)
set(HIREDIS_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/libhiredis_0_13")
set(HIREDIS_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/libhiredis_0_14")
set(LOG4CPP_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/log4cpp1.1.3")
set(LIBPCAP_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/libpcap_1.7.4")
set(MONGO_C_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/mongo_c_driver_1_16_1")
set(MONGO_C_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/mongo_c_driver_1_23_0")
set(CAPNP_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/capnproto_0_8_0")
set(OPENSSL_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/openssl_1_1_1q")
set(GRPC_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/grpc_1_30_2")
set(LIBBPF_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/libbpf_1_0_1")
set(LIBELF_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/elfutils_0_186")
set(PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/protobuf_3.11.4")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} --std=c++2a")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --std=c++2a")
@ -89,7 +90,7 @@ if (DO_NOT_USE_SYSTEM_LIBRARIES_FOR_BUILD)
# Create builds in current folder with install RPATH
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${FASTNETMON_LIBRARIES_GLOBAL_PATH}/libhiredis_0_13/lib;${FASTNETMON_LIBRARIES_GLOBAL_PATH}/log4cpp1.1.3/lib;${FASTNETMON_LIBRARIES_GLOBAL_PATH}/json-c-0.13/lib;${MONGO_C_CUSTOM_INSTALL_PATH}/lib;${MONGO_C_CUSTOM_INSTALL_PATH}/lib64;${GRPC_CUSTOM_INSTALL_PATH}/lib;${FASTNETMON_LIBRARIES_GLOBAL_PATH}/protobuf_3.11.4/lib;${CAPNP_CUSTOM_INSTALL_PATH}/lib;${OPENSSL_CUSTOM_INSTALL_PATH}/lib;${LIBBPF_CUSTOM_INSTALL_PATH};${LIBELF_CUSTOM_INSTALL_PATH}/lib")
SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${FASTNETMON_LIBRARIES_GLOBAL_PATH}/libhiredis_0_14/lib;${LOG4CPP_CUSTOM_INSTALL_PATH}/lib;${MONGO_C_CUSTOM_INSTALL_PATH}/lib;${MONGO_C_CUSTOM_INSTALL_PATH}/lib64;${GRPC_CUSTOM_INSTALL_PATH}/lib;${PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH}/lib;${CAPNP_CUSTOM_INSTALL_PATH}/lib;${OPENSSL_CUSTOM_INSTALL_PATH}/lib;${LIBBPF_CUSTOM_INSTALL_PATH};${LIBELF_CUSTOM_INSTALL_PATH}/lib")
else()
# We do not need any RPATH alterations when we want to link with system libraries (i.e. upstream builds for Debian or RedHat family)
@ -418,8 +419,6 @@ if (LINK_WITH_ABSL)
endif()
if (ENABLE_GOBGP_SUPPORT)
set(PROTOCOL_BUFFERS_CUSTOM_INSTALL_PATH "${FASTNETMON_LIBRARIES_GLOBAL_PATH}/protobuf_3.11.4")
add_definitions(-DENABLE_GOBGP)
add_library(gobgp_action STATIC actions/gobgp_action.cpp)
@ -709,8 +708,6 @@ endif()
### Look for jsonc
target_link_libraries(fast_library ${JSONC_LIBRARY_PATH})
target_link_libraries(fast_library ${OPENSSL_LIBRARY_PATH})
target_link_libraries(fast_library ${OPENSSL_CRYPTO_LIBRARY_PATH})

View File

@ -0,0 +1,159 @@
#!/usr/bin/perl
###
### This tool builds all binary dependencies required for FastNetMon
###
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/perllib";
use Fastnetmon;
use Getopt::Long;
# Retrieve all required modules
BEGIN {
eval { require DateTime; };
if ($@) {
die "DateTime is not installed, install it:\nsudo apt install -y libdatetime-perl\nor\nsudo yum install perl-DateTime\n";
}
}
use DateTime;
# Install:
#
# Debian:
#
# sudo apt-get install -y libdatetime-perl perl
#
# CentOS
# sudo yum install perl perl-Archive-Tar perl-DateTime perl-Env
#
my $library_install_folder = '/opt/fastnetmon-community/libraries';
my $os_type = '';
my $distro_type = '';
my $distro_version = '';
my $distro_architecture = '';
my $appliance_name = '';
my $temp_folder_for_building_project = `mktemp -d /tmp/fastnetmon.build.dir.XXXXXXXXXX`;
chomp $temp_folder_for_building_project;
unless ($temp_folder_for_building_project && -e $temp_folder_for_building_project) {
die "Can't create temp folder in /tmp for building project: $temp_folder_for_building_project\n";
}
# Pass log path to module
$Fastnetmon::install_log_path = '/tmp/fastnetmon_install.log';
# We do not need default very safe permissions
exec_command("chmod 755 $temp_folder_for_building_project");
my $start_time = time();
my $fastnetmon_code_dir = "$temp_folder_for_building_project/fastnetmon/src";
my $cpus_number = 1;
# We could pass options to make with this variable
my $make_options = '';
unless (-e $library_install_folder) {
exec_command("mkdir -p $library_install_folder");
}
main();
### Functions start here
sub main {
my $machine_information = Fastnetmon::detect_distribution();
unless ($machine_information) {
die "Could not collect machine information\n";
}
$distro_version = $machine_information->{distro_version};
$distro_type = $machine_information->{distro_type};
$os_type = $machine_information->{os_type};
$distro_architecture = $machine_information->{distro_architecture};
$appliance_name = $machine_information->{appliance_name};
$Fastnetmon::library_install_folder = $library_install_folder;
$Fastnetmon::temp_folder_for_building_project = $temp_folder_for_building_project;
$cpus_number = Fastnetmon::get_logical_cpus_number();
# We could get huge speed benefits with this option
if ($cpus_number > 1) {
print "You have really nice server with $cpus_number CPU's and we will use they all for build process :)\n";
$make_options = "-j $cpus_number";
}
# Refresh information about packages
if ($distro_type eq 'ubuntu') {
print "Update package manager cache\n";
exec_command("apt-get update");
apt_get('make', 'wget');
} elsif ( $distro_type eq 'centos') {
yum('make', 'wget');
}
# Init environment
init_compiler();
my @required_packages = (
'gcc',
'openssl',
'cmake',
'boost_builder',
'icu',
'boost',
'capnproto',
'hiredis',
'mongo_client',
'protobuf',
'grpc',
'libbpf',
'libelf',
'gobgp',
'log4cpp',
);
for my $package (@required_packages) {
print "Install package $package\n";
my $start_time = DateTime->now();
my $install_res = Fastnetmon::install_package_by_name($package);
my $finished_time = DateTime->now();
my $elapse = $finished_time - $start_time;
# No reasons to print time for fast builds
if ($elapse->in_units('minutes') > 0) {
print "Package build time: " . $elapse->in_units('minutes') . " Minutes\n";
}
unless ($install_res) {
die "Cannot install package $package: $install_res\n";
}
print "\n\n";
}
my $install_time = time() - $start_time;
my $pretty_install_time_in_minutes = sprintf("%.2f", $install_time / 60);
print "We have built project in $pretty_install_time_in_minutes minutes\n";
}

View File

@ -38,4 +38,1325 @@ use Archive::Tar;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(
detect_distribution
init_compiler
exec_command
get_sha1_sum
download_file
read_file
apt_get
yum
get_active_network_interfaces
);
our $dependency_map = {
'boost' => [ 'boost_builder', 'icu' ]
};
my $ld_library_path_for_make = "";
my $build_with_clang = '';
# When we are working with clang it's not a good idea to use stdc++ because they are becoming a bit incompatible
my $use_libcpp_instead_stdcpp = '';
my $gcc_version = '12.1.0';
# We are using this for Boost build system
# 5.3 instead of 5.3.0
my $gcc_version_only_major = $gcc_version;
$gcc_version_only_major =~ s/\.\d$//;
my $gcc_c_compiler_path = '';
my $gcc_cpp_compiler_path = '';
my $clang_c_compiler_path = '';
my $clang_cpp_compiler_path = '';
# By default we haven't any make options
my $make_options = '';
my $boost_version = '1.80.0';
my $boost_version_with_underscore = $boost_version;
$boost_version_with_underscore =~ s/\./_/g;
# We need to know it because we could get huge speed improvements with this option
my $cpus_number = 1;
# We should specify custom compiler path
my $default_c_compiler_path = '';
my $default_cpp_compiler_path = '';
my $os_type = '';
my $distro_type = '';
my $distro_version = '';
my $distro_architecture = '';
my $appliance_name = '';
our $library_install_folder;
our $temp_folder_for_building_project;
our $install_log_path;
# We could store downloaded code here for download time optimization
my $use_cache = 1;
# Path to cache folder
my $cache_folder = '/var/cache/fastnetmon_build_system_cache';
# We need to specify custom compiler options
my $configure_options = '';
# We are using custom version of cmake
my $cmake_path = "/opt/fastnetmon-community/libraries/cmake_3_23_4/bin/cmake";
# We need it for all OpenSSL dependencies
my $openssl_folder_name = "openssl_1_1_1q";
my $current_distro_architecture = `uname -m`;
chomp $current_distro_architecture;
sub exec_command {
my $command = shift;
open my $fl, ">>", $install_log_path;
print {$fl} "We are calling command: $command\n\n";
my $output = `$command 2>&1`;
print {$fl} "Command finished with code $?\n\n";
if ($? == 0) {
return 1;
} else {
warn "Command $command call failed with code $? and output: $output\n";
return '';
}
}
sub get_sha1_sum {
my $path = shift;
if ($os_type eq 'freebsd') {
# # We should not use 'use' here because we haven't this package on non FreeBSD systems by default
require Digest::SHA;
# SHA1
my $sha = Digest::SHA->new(1);
$sha->addfile($path);
return $sha->hexdigest;
}
my $hasher_name = '';
if ($os_type eq 'macosx') {
$hasher_name = 'shasum';
} elsif ($os_type eq 'freebsd') {
$hasher_name = 'sha1';
} else {
# Linux
$hasher_name = 'sha1sum';
}
my $output = `$hasher_name $path`;
chomp $output;
my ($sha1) = ($output =~ m/^(\w+)\s+/);
return $sha1;
}
sub download_file {
my ($url, $path, $expected_sha1_checksumm) = @_;
my ($path_filename, $path_dirs, $path_suffix) = fileparse($path);
my $file_path_in_cache = "$cache_folder/$path_filename";
if ($use_cache) {
unless (-e $cache_folder) {
my $mkdir_result = mkdir $cache_folder;
unless ($mkdir_result) {
warn "Can't create cache folder\n";
return '';
}
}
if (-e $file_path_in_cache) {
if ($expected_sha1_checksumm) {
my $calculated_checksumm = get_sha1_sum($file_path_in_cache);
if ($calculated_checksumm eq $expected_sha1_checksumm) {
my $copy_result = copy($file_path_in_cache, $path);
if (!$copy_result) {
warn "Could not copy file from cache to build folder\n";
} else {
warn "Got archive $path_filename from cache\n";
# All fine!
return 1;
}
} else {
print "Archive from cache has incorrect sha1: $calculated_checksumm expected: $expected_sha1_checksumm\n";
}
} else {
my $copy_result = copy($file_path_in_cache, $path);
if (!$copy_result) {
warn "Could not copy file from cache to build folder\n";
} else {
warn "Got archive $path_filename from cache\n";
# All fine!
return 1;
}
}
}
`wget --no-check-certificate --quiet '$url' -O$path`;
if ($? != 0) {
print "We can't download archive $url correctly\n";
return '';
}
}
if ($expected_sha1_checksumm) {
my $calculated_checksumm = get_sha1_sum($path);
if ($calculated_checksumm eq $expected_sha1_checksumm) {
if ($use_cache) {
# Put file copy to cache folder
my $copy_result = copy($path, $file_path_in_cache);
if (!$copy_result) {
warn "Copy to cache failed\n";
}
}
return 1;
} else {
print "Downloaded archive has incorrect sha1: $calculated_checksumm expected: $expected_sha1_checksumm\n";
return '';
}
} else {
if ($use_cache) {
print "We will copy file from $path to $file_path_in_cache\n";
# Put file copy to cache folder
my $copy_result = copy($path, $file_path_in_cache);
if (!$copy_result) {
warn "Copy to cache failed\n";
}
}
return 1;
}
}
sub init_machine_information {
my $machine_information = Fastnetmon::detect_distribution();
unless ($machine_information) {
die "Could not collect machine information\n";
}
$distro_version = $machine_information->{distro_version};
$distro_type = $machine_information->{distro_type};
$os_type = $machine_information->{os_type};
$distro_architecture = $machine_information->{distro_architecture};
$appliance_name = $machine_information->{appliance_name};
}
# This code will init global compiler settings used in options for other packages build
sub init_compiler {
init_machine_information();
# 530 instead of 5.3.0
my $gcc_version_for_path = $gcc_version;
$gcc_version_for_path =~ s/\.//g;
$gcc_c_compiler_path = "$library_install_folder/gcc$gcc_version_for_path/bin/gcc";
$gcc_cpp_compiler_path = "$library_install_folder/gcc$gcc_version_for_path/bin/g++";
$clang_c_compiler_path = "$library_install_folder/clang_7_0_0/bin/clang";
$clang_cpp_compiler_path = "$library_install_folder/clang_7_0_0/bin/clang++";
# Default compiler path
if ($build_with_clang) {
$default_c_compiler_path = $clang_c_compiler_path;
$default_cpp_compiler_path = $clang_cpp_compiler_path;
} else {
$default_c_compiler_path = $gcc_c_compiler_path;
$default_cpp_compiler_path = $gcc_cpp_compiler_path;
}
# Add new compiler to configure options
# It's mandatory for log4cpp
$configure_options = "CC=$default_c_compiler_path CXX=$default_cpp_compiler_path";
if ($use_libcpp_instead_stdcpp) {
$configure_options = "$configure_options -stdlib=libc++";
}
my @make_library_path_list_options = ("$library_install_folder/gcc$gcc_version_for_path/lib64");
if ($use_libcpp_instead_stdcpp) {
@make_library_path_list_options = ("$library_install_folder/clang_7_0_0/lib");
}
$ld_library_path_for_make = "LD_LIBRARY_PATH=" . join ':', @make_library_path_list_options;
# More detailes about jam lookup: http://www.boost.org/build/doc/html/bbv2/overview/configuration.html
# We use non standard gcc compiler for Boost builder and Boost and specify it this way
open my $fl, ">", "/root/user-config.jam" or die "Can't open $! file for writing manifest\n";
print {$fl} "using gcc : $gcc_version_only_major : $default_cpp_compiler_path ;\n";
close $fl;
# When we run it with vzctl exec we ahve broken env and should put config in /etc too
open my $etcfl, ">", "/etc/user-config.jam" or die "Can't open $! file for writing manifest\n";
print {$etcfl} "using gcc : $gcc_version_only_major : $default_cpp_compiler_path ;\n";
close $etcfl;
# Also we should tune number of threads for make
$cpus_number = get_logical_cpus_number();
# We could get huge speed benefits with this option
if ($cpus_number > 1) {
$make_options = "-j $cpus_number";
}
}
sub install_libbpf {
if ($distro_type eq 'ubuntu' || $distro_type eq 'debian') {
my @dependency_list = ('libelf-dev');
apt_get(@dependency_list);
} elsif ($distro_type eq 'centos') {
yum('elfutils-libelf-devel');
}
my $libbpf_package_install_path = "$library_install_folder/libbpf_1_0_1";
if (-e $libbpf_package_install_path) {
warn "libbpf is installed, skip build\n";
return 1;
}
my $archive_file_name = 'v1.0.1.tar.gz ';
print "Download libbpf\n";
chdir $temp_folder_for_building_project;
my $lib_bpf_download_result = download_file("https://github.com/libbpf/libbpf/archive/refs/tags/v1.0.1.tar.gz", $archive_file_name, '9350f196150892f544e0681cc6c1f78e603b5d95');
unless ($lib_bpf_download_result) {
die "Cannot download linux kernel\n";
}
print "Unpack libbpf\n";
system("tar -xf $archive_file_name");
chdir "libbpf-1.0.1/src";
system("make");
system("mkdir -p $libbpf_package_install_path");
system("cp libbpf.a libbpf.so libbpf.so.1 libbpf.so.1.0.1 $libbpf_package_install_path");
system("mkdir -p $libbpf_package_install_path/include/bpf");
system("cp bpf.h libbpf.h libbpf_common.h libbpf_version.h libbpf_legacy.h $libbpf_package_install_path/include/bpf");
return 1;
}
sub install_gcc {
# 530 instead of 5.3.0
my $gcc_version_for_path = $gcc_version;
$gcc_version_for_path =~ s/\.//g;
my $gcc_package_install_path = "$library_install_folder/gcc$gcc_version_for_path";
if (-e $gcc_package_install_path) {
warn "Found already installed gcc in $gcc_package_install_path. Skip compilation\n";
return '1';
}
if ($distro_type eq 'ubuntu' || $distro_type eq 'debian') {
my @dependency_list = ('libmpfr-dev', 'libmpc-dev', 'libgmp-dev', 'gcc', 'g++');
apt_get(@dependency_list);
} elsif ($distro_type eq 'centos') {
yum('gmp-devel', 'mpfr-devel', 'libmpc-devel', 'gcc', 'gcc-c++');
}
print "Download gcc archive\n";
chdir $temp_folder_for_building_project;
my $archive_file_name = "gcc-$gcc_version.tar.gz";
my $gcc_download_result = download_file("http://ftp.mpi-sb.mpg.de/pub/gnu/mirror/gcc.gnu.org/pub/gcc/releases/gcc-$gcc_version/$archive_file_name", $archive_file_name, '7e79c695a0380ac838fa7c876a121cd28a73a9f5');
unless ($gcc_download_result) {
die "Can't download gcc sources\n";
}
print "Unpack archive\n";
exec_command("tar -xf $archive_file_name");
# Remove source archive
unlink "$archive_file_name";
exec_command("mkdir $temp_folder_for_building_project/gcc-$gcc_version-objdir");
chdir "$temp_folder_for_building_project/gcc-$gcc_version-objdir";
print "Configure build system\n";
# We are using --enable-host-shared because we should build gcc as dynamic library for jit compiler purposes
unless (exec_command("$temp_folder_for_building_project/gcc-$gcc_version/configure --prefix=$gcc_package_install_path --enable-languages=c,c++,jit --enable-host-shared --disable-multilib")) {
warn "Cannot configure gcc\n";
return '';
}
print "Build gcc\n";
unless (exec_command("make $make_options")) {
warn "Cannot make gcc\n";
return '';
}
print "Install gcc\n";
unless (exec_command("make $make_options install")) {
warn "Cannot install gcc\n";
return '';
}
return 1;
}
sub install_boost {
my $boost_install_path = "$library_install_folder/boost_${boost_version_with_underscore}";
if (-e $boost_install_path) {
warn "Boost libraries already exist in $boost_install_path Skip build process\n";
return 1;
}
chdir $library_install_folder;
my $archive_file_name = "boost_${boost_version_with_underscore}.tar.gz";
print "Install Boost dependencies\n";
if ($distro_type eq 'ubuntu' || $distro_type eq 'debian') {
apt_get('bzip2');
} elsif ($distro_type eq 'centos') {
yum('bzip2');
}
print "Download Boost source code\n";
my $boost_download_result = download_file("https://boostorg.jfrog.io/artifactory/main/release/$boost_version/source/boost_${boost_version_with_underscore}.tar.bz2", $archive_file_name, '31209dcff292bd6a64e5e08ceb3ce44a33615dc0');
unless ($boost_download_result) {
die "Can't download Boost source code\n";
}
print "Unpack Boost source code\n";
exec_command("tar -xf $archive_file_name");
my $folder_name_inside_archive = "boost_$boost_version_with_underscore";
print "Fix permissions\n";
# Fix permissions because they are broken inside official archive
exec_command("find $folder_name_inside_archive -type f -exec chmod 644 {} \\;");
exec_command("find $folder_name_inside_archive -type d -exec chmod 755 {} \\;");
exec_command("chown -R root:root $folder_name_inside_archive");
print "Remove archive\n";
unlink "$archive_file_name";
chdir $folder_name_inside_archive;
print "Build Boost\n";
# We have troubles when run this code with vzctl exec so we should add custom compiler in path
# So without HOME=/root nothing worked correctly due to another "openvz" feature
my $b2_build_result = exec_command("HOME=/root PATH=\$PATH:$library_install_folder/gcc1120/bin $library_install_folder/boost_build1.72.0/bin/b2 -j$cpus_number -sICU_PATH=$library_install_folder/libicu_65_1 --build-dir=$temp_folder_for_building_project/boost_build_temp_directory_$boost_version_with_underscore toolset=gcc-$gcc_version_only_major link=shared --without-test --without-python --without-wave --without-log --without-mpi");
# We should not do this check because b2 build return bad return code even in success case... when it can't build few non important targets
unless ($b2_build_result) {
### die "Can't execute b2 build correctly\n";
}
1;
}
sub install_boost_builder {
chdir $temp_folder_for_building_project;
# We use another name because it uses same name as boost distribution
my $archive_file_name = 'boost-1.72.0.tar.gz';
my $boost_builder_install_folder = "$library_install_folder/boost_build1.72.0";
if (-e $boost_builder_install_folder) {
warn "Found installed Boost builder at $boost_builder_install_folder\n";
return 1;
}
print "Download boost builder\n";
my $boost_build_result = download_file("https://github.com/boostorg/build/archive/boost-1.72.0.tar.gz", $archive_file_name,
'8d4aede249cc414f5f375423e26feca99f1c1088');
unless ($boost_build_result) {
die "Can't download boost builder\n";
}
print "Unpack boost builder\n";
exec_command("tar -xf $archive_file_name");
unless (chdir "build-boost-1.72.0") {
die "Cannot do chdir to build boost folder\n";
}
print "Build Boost builder\n";
# We haven't system compiler here and we will use custom gcc for compilation here
my $bootstrap_result = exec_command("CC=$default_c_compiler_path CXX=$default_cpp_compiler_path ./bootstrap.sh --with-toolset=gcc");
unless ($bootstrap_result) {
die "bootstrap of Boost Builder failed, please check logs\n";
}
# We should specify toolset here if we want to do build with custom compiler
# We have troubles when run this code with vzctl exec so we should add custom compiler in path
my $b2_install_result = exec_command("./b2 install --prefix=$boost_builder_install_folder");
unless ($b2_install_result) {
die "Can't execute b2 install\n";
}
1;
}
sub install_log4cpp {
my $log_cpp_version_short = '1.1.3';
my $log4cpp_install_path = "$library_install_folder/log4cpp" . $log_cpp_version_short;
if (-e $log4cpp_install_path) {
warn "We have log4cpp already, skip build\n";
return 1;
}
my $distro_file_name = "log4cpp-$log_cpp_version_short.tar.gz";
my $log4cpp_url = "https://sourceforge.net/projects/log4cpp/files/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-$log_cpp_version_short.tar.gz/download";
chdir $temp_folder_for_building_project;
print "Download log4cpp sources\n";
my $log4cpp_download_result = download_file($log4cpp_url, $distro_file_name, '74f0fea7931dc1bc4e5cd34a6318cd2a51322041');
unless ($log4cpp_download_result) {
die "Can't download log4cpp\n";
}
print "Unpack log4cpp sources\n";
exec_command("tar -xf $distro_file_name");
chdir "$temp_folder_for_building_project/log4cpp";
if ($distro_architecture eq 'aarch64') {
# TODO: unfortunately, I removed these files and we need to switch to master build: https://git.code.sf.net/p/log4cpp/codegit
# commit: 2e117d81e94ec4f9c5af42fcf76a0583a036e106
# For arm64 build we need multiple fixes
# checking build system type... config/config.guess: unable to guess system type
# configure: error: cannot guess build type; you must specify one
exec_command("curl https://raw.githubusercontent.com/pavel-odintsov/config/master/config.guess -o./config/config.guess");
exec_command("curl https://raw.githubusercontent.com/pavel-odintsov/config/master/config.sub -o./config/config.sub");
}
print "Build log4cpp\n";
# TODO: we need some more reliable way to specify options here
if ($configure_options) {
exec_command("$configure_options ./configure --prefix=$log4cpp_install_path");
} else {
exec_command("./configure --prefix=$log4cpp_install_path");
}
my $make_result = exec_command("make $make_options install");
if (!$make_result) {
print "Make for log4cpp failed\n";
}
1;
}
sub install_grpc {
my $grpc_git_commit = "v1.30.2";
my $grpc_install_path = "$library_install_folder/grpc_1_30_2";
if (-e $grpc_install_path) {
print "gRPC is already installed, skip compilation\n";
return 1;
}
chdir $temp_folder_for_building_project;
print "Clone gRPC repository\n";
exec_command("git clone https://github.com/grpc/grpc.git");
chdir "grpc";
# For back compatibility with old git
exec_command("git checkout $grpc_git_commit");
print "Update project submodules\n";
exec_command("git submodule update --init");
### Patch makefile for custom gcc: https://github.com/grpc/grpc/issues/3893
exec_command("sed -i '81i DEFAULT_CC=$default_c_compiler_path' Makefile");
exec_command("sed -i '82i DEFAULT_CXX=$default_cpp_compiler_path' Makefile");
print "Build gRPC\n";
my $make_result = exec_command("$ld_library_path_for_make make $make_options");
unless ($make_result) {
die "Could not build gRPC: make failed";
}
print "Install gRPC\n";
exec_command("$ld_library_path_for_make make install prefix=$grpc_install_path");
1;
}
# Get git repository source code
sub git_clone_repository {
my ($repo_url, $local_folder, $repo_commit) = @_;
my $git_clone_result = exec_command("git clone $repo_url $local_folder");
unless ($git_clone_result) {
warn "Could not clone repository: $repo_url\n";
return '';
}
# If we want certain commit
if ($repo_commit ne 'master') {
# Change current working directory to git repo
chdir "$local_folder";
my $checkout_branch_result = exec_command("git checkout $repo_commit");
unless ($checkout_branch_result) {
warn "Could not checkout commit $repo_commit for repository $repo_url\n";
return '';
}
}
return 1;
}
sub install_gobgp {
chdir $temp_folder_for_building_project;
my $distro_file_name = 'gobgp_2.27.0_linux_amd64.tar.gz';
my $download_result = download_file("https://github.com/osrg/gobgp/releases/download/v2.27.0/$distro_file_name",
$distro_file_name, 'dd906c552a727d3f226f3851bf2c92bfdafb92a7');
unless ($download_result) {
die "Could not download gobgp\n";
}
my $unpack_result = exec_command("tar -xf $distro_file_name");
unless ($unpack_result) {
die "Could not unpack gobgp\n";
}
my $gobgp_install_path = "$library_install_folder/gobgp_2_27_0";
mkdir "$gobgp_install_path";
`cp gobgp $gobgp_install_path`;
`cp gobgpd $gobgp_install_path`;
1;
}
sub install_protobuf {
my $protobuf_install_path = "$library_install_folder/protobuf_3.11.4";
if (-e $protobuf_install_path) {
warn "Found installed Protobuf, skip compilation\n";
return 1;
}
if ($distro_type eq 'ubuntu' || $distro_type eq 'debian') {
apt_get('make', 'autoconf', 'automake', 'git', 'libtool', 'curl');
} elsif ($distro_type eq 'centos') {
yum('make', 'autoconf', 'automake', 'git', 'libtool', 'curl');
}
my $distro_file_name = 'protobuf-all-3.11.4.tar.gz';
chdir $temp_folder_for_building_project;
print "Download protocol buffers\n";
my $protobuf_download_result = download_file("https://github.com/protocolbuffers/protobuf/releases/download/v3.11.4/$distro_file_name",
$distro_file_name, '318f4d044078285db7ae69b68e77f148667f98f4');
unless ($protobuf_download_result) {
die "Can't download protobuf\n";
}
print "Unpack protocol buffers\n";
exec_command("tar -xf $distro_file_name");
chdir "protobuf-3.11.4";
print "Configure protobuf\n";
print "Execute autogen\n";
exec_command("./autogen.sh");
exec_command("$configure_options ./configure --prefix=$protobuf_install_path");
print "Build protobuf\n";
# We have specified LD_LIBRARY path for fixing issue with version `GLIBCXX_3.4.21' not found
exec_command("$ld_library_path_for_make make $make_options install");
1;
}
sub install_libelf {
if (-e "$library_install_folder/elfutils_0_186") {
warn "elfutils already exists\n";
return 1;
}
if ($distro_type eq 'ubuntu' || $distro_type eq 'debian') {
apt_get(('zlib1g-dev'));
} elsif ($distro_type eq 'centos') {
yum('zlib-devel', 'm4');
}
my $res = install_configure_based_software("https://sourceware.org/pub/elfutils/0.186/elfutils-0.186.tar.bz2", "650d52024be684dabf18a5261a69836a16f84f72", "$library_install_folder/elfutils_0_186", '--disable-debuginfod --disable-libdebuginfod');
unless ($res) {
die "Cannot install elfutils\n";
}
return 1;
}
sub install_capnproto {
my $capnp_install_path = "$library_install_folder/capnproto_0_8_0";
if (-e $capnp_install_path) {
warn "Cap'n'proto already existis, skip compilation\n";
return 1;
}
my $res = install_configure_based_software("https://capnproto.org/capnproto-c++-0.8.0.tar.gz",
"fbc1c65b32748029f1a09783d3ebe9d496d5fcc4", $capnp_install_path,
'');
unless ($res) {
die "Could not install capnproto";
}
return 1;
}
sub install_mongo_client {
my $install_path = "$library_install_folder/mongo_c_driver_1_23_0";
if (-e $install_path) {
warn "mongo_c_driver is already installed, skip build";
return 1;
}
my $openssl_path = "$library_install_folder/$openssl_folder_name";
# OpenSSL is mandatory for SCRAM-SHA-1 auth mode
# I also use flag ENABLE_ICU=OFF to disable linking against icu system library. I do no think that we really need it
my $res = install_cmake_based_software("https://github.com/mongodb/mongo-c-driver/releases/download/1.23.0/mongo-c-driver-1.23.0.tar.gz",
"f6256acfe89ed094158be84a3ce2a56fd7f22637",
$install_path,
"$cmake_path -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:STRING=$library_install_folder/mongo_c_driver_1_23_0 -DCMAKE_C_COMPILER=$default_c_compiler_path -DOPENSSL_ROOT_DIR=$openssl_path -DCMAKE_CXX_COMPILER=$default_cpp_compiler_path -DENABLE_ICU=OFF -DMONGOC_TEST_USE_CRYPT_SHARED=OFF ..");
if (!$res) {
die "Could not install mongo c client\n";
}
return 1;
}
sub install_configure_based_software {
my ($url_to_archive, $sha1_summ_for_archive, $library_install_path, $configure_options) = @_;
unless ($url_to_archive && $sha1_summ_for_archive && $library_install_path) {
warn "You haven't specified all mandatory arguments for install_configure_based_software\n";
return '';
}
unless (defined($configure_options)) {
$configure_options = '';
}
chdir $temp_folder_for_building_project;
my $file_name = get_file_name_from_url($url_to_archive);
unless ($file_name) {
die "Could not extract file name from URL $url_to_archive";
}
print "Download archive\n";
my $archive_download_result = download_file($url_to_archive, $file_name, $sha1_summ_for_archive);
unless ($archive_download_result) {
die "Could not download URL $url_to_archive";
}
unless (-e $file_name) {
die "Could not find downloaded file in current folder";
}
print "Read file list inside archive\n";
my $folder_name_inside_archive = get_folder_name_inside_archive("$temp_folder_for_building_project/$file_name");
unless ($folder_name_inside_archive) {
die "We could not extract folder name from tar archive '$temp_folder_for_building_project/$file_name'\n";
}
print "Unpack archive\n";
my $unpack_result = exec_command("tar -xf $file_name");
unless ($unpack_result) {
die "Unpack failed";
}
chdir $folder_name_inside_archive;
unless (-e "configure") {
die "We haven't configure script here";
}
print "Execute configure\n";
my $configure_command = "CC=$default_c_compiler_path CXX=$default_cpp_compiler_path ./configure --prefix=$library_install_path $configure_options";
my $configure_result = exec_command($configure_command);
unless ($configure_result) {
die "Configure failed";
}
### TODO: this is ugly thing! But here you could find hack for poco libraries
if ($url_to_archive =~ m/Poco/i) {
exec_command("sed -i 's#^CC .*#CC = $default_c_compiler_path#' build/config/Linux");
exec_command("sed -i 's#^CXX .*#CXX = $default_cpp_compiler_path#' build/config/Linux");
#print `cat build/config/Linux`;
}
# librdkafka does not like make+make install approach, we should use them one by one
print "Execute make\n";
my $make_result = exec_command("$ld_library_path_for_make make $make_options");
unless ($make_result) {
die "Make failed";
}
print "Execute make install\n";
# We explicitly added path to library folders from our custom compiler here
my $make_install_result = exec_command("$ld_library_path_for_make make install");
unless ($make_install_result) {
die "Make install failed";
}
return 1;
}
sub install_openssl {
my $distro_file_name = 'openssl-1.1.1q.tar.gz';
my $openssl_install_path = "$library_install_folder/openssl_1_1_1q";
if (-e $openssl_install_path) {
warn "We found already installed openssl in folder $openssl_install_path Skip compilation\n";
return 1;
}
chdir $temp_folder_for_building_project;
my $openssl_download_result = download_file("https://www.openssl.org/source/$distro_file_name",
$distro_file_name, '79511a8f46f267c533efd32f22ad3bf89a92d8e5');
unless ($openssl_download_result) {
die "Could not download openssl";
}
exec_command("tar -xf $distro_file_name");
chdir "openssl-1.1.1q";
exec_command("./config shared --prefix=$openssl_install_path");
exec_command("make -j $make_options");
exec_command("make install");
1;
}
sub install_icu {
my $distro_file_name = 'icu4c-65_1-src.tgz';
chdir $temp_folder_for_building_project;
my $icu_install_path = "$library_install_folder/libicu_65_1";
if (-e $icu_install_path) {
warn "Found installed icu at $icu_install_path\n";
return 1;
}
print "Download icu\n";
my $icu_download_result = download_file("https://github.com/unicode-org/icu/releases/download/release-65-1/$distro_file_name",
$distro_file_name, 'd1e6b58aea606894cfb2495b6eb1ad533ccd2a25');
unless ($icu_download_result) {
die "Could not download ibicu";
}
print "Unpack icu\n";
exec_command("tar -xf $distro_file_name");
chdir "icu/source";
print "Build icu\n";
exec_command("$configure_options ./configure --prefix=$icu_install_path");
exec_command("$ld_library_path_for_make make $make_options");
exec_command("$ld_library_path_for_make make $make_options install");
1;
}
sub install_cmake {
print "Install cmake\n";
my $cmake_install_path = "$library_install_folder/cmake_3_23_4";
if (-e $cmake_install_path) {
warn "Found installed cmake at $cmake_install_path\n";
return 1;
}
my $distro_file_name = "cmake-3.23.4.tar.gz";
chdir $temp_folder_for_building_project;
print "Download archive\n";
my $cmake_download_result = download_file("https://github.com/Kitware/CMake/releases/download/v3.23.4/$distro_file_name", $distro_file_name, '05957280718e068df074f76c89cba77de1ddd4a2');
unless ($cmake_download_result) {
die "Can't download cmake\n";
}
exec_command("tar -xf $distro_file_name");
chdir "cmake-3.23.4";
my $openssl_path = "$library_install_folder/$openssl_folder_name";
print "Execute bootstrap, it will need time\n";
my $boostrap_result = exec_command("./bootstrap --prefix=$cmake_install_path --parallel=$cpus_number -- -DOPENSSL_ROOT_DIR=$openssl_path");
unless ($boostrap_result) {
die("Cannot run bootstrap\n");
}
print "Make it\n";
my $make_command = "make $make_options";
my $make_result = exec_command($make_command);
unless ($make_result) {
die "Make command '$make_command' failed\n";
}
exec_command("make install");
return 1;
}
# Extract file name from URL
# https://github.com/mongodb/mongo-cxx-driver/archive/r3.0.0-rc0.tar.gz => r3.0.0-rc0.tar.gz
sub get_file_name_from_url {
my $url = shift;
# Remove prefix
$url =~ s#https?://##;
my @components = split '/', $url;
return $components[-1];
}
sub install_cmake_based_software {
my ($url_to_archive, $sha1_summ_for_archive, $library_install_path, $cmake_with_options) = @_;
unless ($url_to_archive && $sha1_summ_for_archive && $library_install_path && $cmake_with_options) {
return '';
}
chdir $temp_folder_for_building_project;
my $file_name = get_file_name_from_url($url_to_archive);
unless ($file_name) {
die "Could not extract file name from URL $url_to_archive";
}
print "Download archive\n";
my $archive_download_result = download_file($url_to_archive, $file_name, $sha1_summ_for_archive);
unless ($archive_download_result) {
die "Could not download URL $url_to_archive";
}
unless (-e $file_name) {
die "Could not find downloaded file in current folder";
}
print "Read file list inside archive\n";
my $folder_name_inside_archive = get_folder_name_inside_archive("$temp_folder_for_building_project/$file_name");
unless ($folder_name_inside_archive) {
die "We could not extract folder name from tar archive: $temp_folder_for_building_project/$file_name\n";
}
print "Unpack archive\n";
my $unpack_result = exec_command("tar -xf $file_name");
unless ($unpack_result) {
die "Unpack failed";
}
chdir $folder_name_inside_archive;
unless (-e "CMakeLists.txt") {
die "We haven't CMakeLists.txt in top project folder! Could not build project";
}
unless (-e "build") {
mkdir "build";
}
chdir "build";
print "Generate make file with cmake\n";
# print "cmake command: $cmake_with_options\n";
my $cmake_result = exec_command($cmake_with_options);
unless ($cmake_result) {
die "cmake command failed";
}
print "Build project with make\n";
my $make_command = "make $make_options";
my $make_result = exec_command($make_command);
unless ($make_result) {
die "Make command '$make_command' failed\n";
}
print "Install project to target directory\n";
my $install_result = exec_command("make install");
unless ($install_result) {
die "Install failed";
}
return 1;
}
# Get folder name from archive
sub get_folder_name_inside_archive {
my $file_path = shift;
unless ($file_path && -e $file_path) {
return '';
}
my $tar = Archive::Tar->new;
$tar->read($file_path);
for my $file($tar->list_files()) {
# if name has / in the end we could assume it's folder
if ($file =~ m#/$#) {
return $file;
}
}
# For some reasons we can have case when we do not have top level folder alone but we can extract it from path:
# libcmime-0.2.1/VERSION
for my $file($tar->list_files()) {
# if name has / in the end we could assume it's folder
if ($file =~ m#(.*?)/.*+$#) {
return $1;
}
}
return '';
}
sub install_hiredis {
my $disto_file_name = 'v0.14.0.tar.gz';
my $hiredis_install_path = "$library_install_folder/libhiredis_0_14";
if (-e $hiredis_install_path) {
warn "hiredis is found at $hiredis_install_path skip build\n";
return 1;
}
chdir $temp_folder_for_building_project;
print "Download hiredis\n";
my $hiredis_download_result = download_file("https://github.com/redis/hiredis/archive/$disto_file_name",
$disto_file_name, 'd668b86756d2c68f0527e845dc10ace5a053bbd9');
unless ($hiredis_download_result) {
die "Can't download hiredis\n";
}
exec_command("tar -xf $disto_file_name");
print "Build hiredis\n";
chdir "hiredis-0.14.0";
exec_command("PREFIX=$hiredis_install_path make $make_options install");
1;
}
sub read_file {
my $file_name = shift;
my $res = open my $fl, "<", $file_name;
unless ($res) {
return "";
}
my $content = join '', <$fl>;
chomp $content;
return $content;
}
sub apt_get {
my @packages_list = @_;
# We install one package per apt-get call because installing multiple packages in one time could fail of one package is broken
for my $package (@packages_list) {
exec_command("apt-get install -y --force-yes $package");
if ($? != 0) {
print "Package '$package' install failed with code $?\n"
}
}
}
sub yum {
my @packages_list = @_;
for my $package (@packages_list) {
exec_command("yum install -y $package");
if ($? != 0) {
print "Package '$package' install failed with code $?\n";
}
}
}
sub get_logical_cpus_number {
if ($os_type eq 'linux') {
my @cpuinfo = `cat /proc/cpuinfo`;
chomp @cpuinfo;
my $cpus_number = scalar grep {/processor/} @cpuinfo;
return $cpus_number;
} elsif ($os_type eq 'macosx' or $os_type eq 'freebsd') {
my $cpus_number = `sysctl -n hw.ncpu`;
chomp $cpus_number;
}
}
# Detect operating system of this machine
sub detect_distribution {
my $os_type = '';
my $distro_type = '';
my $distro_version = '';
my $appliance_name = '';
my $distro_architecture = '';
my $uname_s_output = `uname -s`;
chomp $uname_s_output;
# uname -a output examples:
# FreeBSD 10.1-STABLE FreeBSD 10.1-STABLE #0 r278618: Thu Feb 12 13:55:09 UTC 2015 root@:/usr/obj/usr/src/sys/KERNELWITHNETMAP amd64
# Darwin MacBook-Pro-Pavel.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64
# Linux ubuntu 3.16.0-30-generic #40~14.04.1-Ubuntu SMP Thu Jan 15 17:43:14 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
if ($uname_s_output =~ /FreeBSD/) {
$os_type = 'freebsd';
} elsif ($uname_s_output =~ /Darwin/) {
$os_type = 'macosx';
} elsif ($uname_s_output =~ /Linux/) {
$os_type = 'linux';
} else {
warn "Can't detect platform operating system\n";
}
if ($os_type eq 'linux') {
# x86_64 or i686
$distro_architecture = `uname -m`;
chomp $distro_architecture;
if (-e "/etc/debian_version") {
# Well, on this step it could be Ubuntu or Debian
# We need check issue for more details
my @issue = `cat /etc/issue`;
chomp @issue;
my $issue_first_line = $issue[0];
# Possible /etc/issue contents:
# Debian GNU/Linux 8 \n \l
# Ubuntu 14.04.2 LTS \n \l
# Welcome to VyOS - \n \l
my $is_proxmox = '';
# Really hard to detect https://github.com/proxmox/pve-manager/blob/master/bin/pvebanner
for my $issue_line (@issue) {
if ($issue_line =~ m/Welcome to the Proxmox Virtual Environment/) {
$is_proxmox = 1;
$appliance_name = 'proxmox';
last;
}
}
if ($issue_first_line =~ m/Debian/ or $is_proxmox) {
$distro_type = 'debian';
$distro_version = `cat /etc/debian_version`;
chomp $distro_version;
# Debian 6 example: 6.0.10
# We will try transform it to decimal number
if ($distro_version =~ /^(\d+\.\d+)\.\d+$/) {
$distro_version = $1;
}
} elsif ($issue_first_line =~ m/Ubuntu (\d+(?:\.\d+)?)/) {
$distro_type = 'ubuntu';
$distro_version = $1;
} elsif ($issue_first_line =~ m/VyOS/) {
# Yes, VyOS is a Debian
$distro_type = 'debian';
$appliance_name = 'vyos';
my $vyos_distro_version = `cat /etc/debian_version`;
chomp $vyos_distro_version;
# VyOS have strange version and we should fix it
if ($vyos_distro_version =~ /^(\d+)\.\d+\.\d+$/) {
$distro_version = $1;
}
}
}
if (-e "/etc/redhat-release") {
$distro_type = 'centos';
my $distro_version_raw = `cat /etc/redhat-release`;
chomp $distro_version_raw;
# CentOS 6:
# CentOS release 6.6 (Final)
# CentOS 7:
# CentOS Linux release 7.0.1406 (Core)
# Fedora release 21 (Twenty One)
if ($distro_version_raw =~ /(\d+)/) {
$distro_version = $1;
}
}
if (-e "/etc/gentoo-release") {
$distro_type = 'gentoo';
my $distro_version_raw = `cat /etc/gentoo-release`;
chomp $distro_version_raw;
}
unless ($distro_type) {
die "This distro is unsupported, please do manual install";
}
print "We detected your OS as $distro_type Linux $distro_version\n";
} elsif ($os_type eq 'macosx') {
my $mac_os_versions_raw = `sw_vers -productVersion`;
chomp $mac_os_versions_raw;
if ($mac_os_versions_raw =~ /(\d+\.\d+)/) {
$distro_version = $1;
}
print "We detected your OS as Mac OS X $distro_version\n";
} elsif ($os_type eq 'freebsd') {
my $freebsd_os_version_raw = `uname -r`;
chomp $freebsd_os_version_raw;
if ($freebsd_os_version_raw =~ /^(\d+)\.?/) {
$distro_version = $1;
}
print "We detected your OS as FreeBSD $distro_version\n";
}
return {
'distro_version' => $distro_version,
'distro_type' => $distro_type,
'os_type' => $os_type,
'distro_architecture' => $distro_architecture,
'appliance_name' => $appliance_name,
};
}
sub install_package_by_name_with_dependencies {
my $package_name = shift;
my @dependency_packages = ();
die "Please specify package name" unless $package_name;
if (defined $dependency_map->{$package_name} && $dependency_map->{$package_name}) {
unless (ref $dependency_map->{$package_name} eq 'ARRAY') {
die "Dependency list should be array!\n"
}
@dependency_packages = @{ $dependency_map->{$package_name} };
print "We have dependencies for this package: " . (join ',', @dependency_packages) . "\n";
}
my @pckages_for_install = (@dependency_packages, $package_name);
for my $package (@pckages_for_install) {
install_package_by_name($package);
}
}
sub install_package_by_name {
my $package_name = shift;
unless (defined( &{ "install_$package_name" } )) {
die "We haven't handler function for this library: $package_name\n";
}
no strict 'refs';
&{ "install_$package_name"}();
use strict 'refs';
}
1;