Alien-Libtensorflow
view release on metacpan or search on metacpan
use alienfile;
use strict;
use warnings;
use Config;
use File::Spec;
use File::Find;
use File::Copy;
use Path::Tiny;
use URI;
# Settings:
#
# - ALIEN_LIBTENSORFLOW_FROM_BINARY_VERSION
#
# Filter of version to download (e.g., "2.9.3" to download
# libtensorflow v2.9.3).
#
# - ALIEN_LIBTENSORFLOW_FROM_BINARY_ALLOW_RELEASE_CANDIDATES
#
# Boolean that allows release candidate binary releases when true (default: 0).
#
# - ALIEN_LIBTENSORFLOW_FROM_SOURCE
#
# Boolean to control if libtensorflow is built from source (default: 0).
#
# When set to 0, will attempt to download binary if available for platform
# and builds from source otherwise.
#
# When set to 1, always builds from source.
#
# - ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE
#
# Which type of build to create.
#
# Values:
# - "release": release build (default)
#
# This has no debugging symbols.
#
# - "debug": debug build
#
# - ALIEN_LIBTENSORFLOW_DEVICE_TYPE
#
# Which device type to use for binary release.
#
# Values:
# - "auto": automatically detect type (default)
# - "cpu": CPU
# - "gpu": GPU (Currently only Nvidia)
use Env qw(
ALIEN_LIBTENSORFLOW_FROM_BINARY_VERSION
ALIEN_LIBTENSORFLOW_FROM_BINARY_ALLOW_RELEASE_CANDIDATES
ALIEN_LIBTENSORFLOW_FROM_SOURCE
ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE
ALIEN_LIBTENSORFLOW_DEVICE_TYPE
@PATH
);
my %os_arch_data = (
'linux:x86_64' => {
device_type => {
cpu => {
bucket_prefix => 'libtensorflow/libtensorflow-cpu-linux-x86_64',
bucket_format => 'tar.gz',
},
gpu => {
bucket_prefix => 'libtensorflow/libtensorflow-gpu-linux-x86_64',
bucket_format => 'tar.gz',
},
}
},
'darwin:x86_64' => {
device_type => {
cpu => {
bucket_prefix => 'libtensorflow/libtensorflow-cpu-darwin-x86_64',
bucket_format => 'tar.gz',
}
},
},
'MSWin32:x86_64' => {
device_type => {
cpu => {
bucket_prefix => 'libtensorflow/libtensorflow-cpu-windows-x86_64',
bucket_format => 'zip',
},
gpu => {
bucket_prefix => 'libtensorflow/libtensorflow-gpu-windows-x86_64',
bucket_format => 'zip',
},
}
},
);
my %os_dynamic_lib = (
'linux' => 'libtensorflow.so.2',
'darwin' => 'libtensorflow.2.dylib',
'MSWin32' => 'tensorflow.dll',
);
my %other_os_dynamic_lib = (
'linux' => 'libtensorflow_framework.so.2',
'darwin' => 'libtensorflow_framework.2.dylib',
);
probe sub {
# linux: ./lib/libtensorflow.so.2
# darwin: ./lib/libtensorflow.2.dylib
# win32: ./lib/tensorflow.dll
my @prefix = ( "/usr/local" );
for my $prefix (@prefix) {
my $dylib_path = File::Spec->catfile(
$prefix, qw(lib),
$os_dynamic_lib{ $^O }
);
return 'system' if -f $dylib_path;
}
return 'share';
};
sub detect_gpu {
# detect Nvidia
if( $^O eq 'linux' ) {
return !! File::Which::which('nvidia-smi');
} elsif( $^O eq 'MSWin32' ) {
local $ENV{PATH} = $ENV{PATH};
push @PATH, File::Spec->catfile($ENV{ProgramFiles}, 'NVIDIA Corporation','NVSMI' );
return !! File::Which::which('nvidia-smi');
}
return 0;
}
sub device_type {
return 'gpu' if detect_gpu;
return 'cpu';
}
share {
requires 'HTTP::Tiny' => 0;
requires 'Net::SSLeay' => 0;
requires 'IO::Socket::SSL' => 0;
requires 'URI' => 0;
requires 'File::Which';
requires 'Alien::Build::Plugin::Download::GitHub' => 0.10;
$ENV{ALIEN_LIBTENSORFLOW_FROM_BINARY_ALLOW_RELEASE_CANDIDATES} ||= 0;
# 0|1 (default: 0)
$ENV{ALIEN_LIBTENSORFLOW_FROM_SOURCE} ||= 0;
# release|debug (default: release)
$ENV{ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE} ||= 'release';
die "Unknown value for ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE = $ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE"
unless $ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE =~ /^(release|debug)$/;
# auto|cpu|gpu (default: auto)
$ENV{ALIEN_LIBTENSORFLOW_DEVICE_TYPE} ||= 'auto';
die "Unknown value for ALIEN_LIBTENSORFLOW_DEVICE_TYPE = $ALIEN_LIBTENSORFLOW_DEVICE_TYPE"
unless $ALIEN_LIBTENSORFLOW_DEVICE_TYPE =~ /^(auto|cpu|gpu)$/;
my $os_arch = join ":", ( $^O, meta->prop->{platform}{cpu}{arch}{name} );
if(exists $os_arch_data{$os_arch} && !$ENV{ALIEN_LIBTENSORFLOW_FROM_SOURCE}) {
my $device_type = $ALIEN_LIBTENSORFLOW_DEVICE_TYPE eq 'auto'
? device_type()
: $ALIEN_LIBTENSORFLOW_DEVICE_TYPE;
die "Binary release for $os_arch + $device_type does not exist"
unless exists $os_arch_data{$os_arch}{device_type}{$device_type};
my $data = $os_arch_data{$os_arch}{device_type}{$device_type};
my $version_filter = exists $ENV{ALIEN_LIBTENSORFLOW_FROM_BINARY_VERSION}
? $ALIEN_LIBTENSORFLOW_FROM_BINARY_VERSION
: '2.';
(my $bucket_prefix_no_dir = $data->{bucket_prefix}) =~ s,^libtensorflow/,,;
my $re = qr{
^
\Q@{[ $bucket_prefix_no_dir ]}\E
-
(?<version> .* )
\.
\Q@{[ $data->{bucket_format} ]}\E
$
}x;
my $bucket_url = 'https://storage.googleapis.com/tensorflow/';
my $start_url = URI->new($bucket_url);
$start_url->query_form( prefix => join('',
$data->{bucket_prefix},
'-',
$version_filter,
));
start_url $start_url;
plugin 'Decode::Mojo';
meta->around_hook( fetch => sub {
my $orig = shift;
my $build = shift;
my $fetched = $orig->($build, @_);
if( $fetched->{filename} eq 'tensorflow' ) {
# bucket data
$fetched->{content} = path($fetched->{path})->slurp_raw if ! exists $fetched->{content} && exists $fetched->{path};
my $xml = $build->meta_prop->{plugin_decode_mojo_class}->new( $fetched->{content} );
my $list = $xml->find('Key')->map(sub{
my $content = $_[0]->content;
(my $filename = $content) =~ s,^libtensorflow/,,;
my $bucket_item = {
filename => $filename,
version => do { $filename =~ $re && $1 },
url => do { join '', $bucket_url, $content },
protocol => 'https',
};
my ($build) = @_;
my $lib_dir = 'lib';
# This is because ExtUtils::Install uses File::Copy::copy()
# which does not handle symlinks (it copies the
# contents of what the symlinks point to).
$build->log("Only keep one copy of library, no symlinks");
for my $lib ( map { exists $_->{$^O} ? $_->{$^O} : () } \%os_dynamic_lib, \%other_os_dynamic_lib ) {
my $lib_symlink = File::Spec->catfile($lib_dir, $lib );
next unless -l $lib_symlink;
$build->log( "Processing $lib" );
my $lib_file = $lib_symlink;
$lib_file = File::Spec->rel2abs(readlink $lib_file, $lib_dir) while -l $lib_file;
unlink $lib_symlink;
File::Copy::move($lib_file , $lib_symlink);
}
my @symlinks;
find(sub { push @symlinks, $File::Find::name if -l }, $lib_dir);
unlink @symlinks;
},
];
plugin 'Build::Copy';
meta->after_hook( build => sub {
my($build) = @_;
$build->runtime_prop->{'style'} = 'binary';
$build->runtime_prop->{'device_type'} = $device_type;
});
gather sub {
my($build) = @_;
my $prefix = $build->runtime_prop->{prefix};
my $include_path = File::Spec->catfile($prefix, qw(include));
my $lib_path = File::Spec->catfile($prefix, qw(lib));
my $cflags = "-I$include_path";
my @ldlibs = "-ltensorflow";
my $libs = join " ", "-L$lib_path", @ldlibs;
$build->runtime_prop->{cflags} = $cflags;
$build->runtime_prop->{libs} = $libs;
};
} else {
requires 'Alien::Bazel';
plugin 'Download::GitHub' => (
github_user => 'tensorflow',
github_repo => 'tensorflow',
);
build [
sub {
my ($build) = @_;
$build->install_prop->{libtensorflow_dynlib} = $os_dynamic_lib{$^O};
},
[
qw(bazel build),
'--verbose_failures',
( $ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE eq 'debug' ? q(--config=dbg) : () ),
'//tensorflow:%{.install.libtensorflow_dynlib}'
],
'%{make_path} %{.install.stage}/lib',
"%{cp} bazel-bin/tensorflow/%{.install.libtensorflow_dynlib} %{.install.stage}/lib",
];
meta->after_hook( build => sub {
my($build) = @_;
$build->runtime_prop->{'style'} = 'source';
$build->runtime_prop->{'build_type'} = $ALIEN_LIBTENSORFLOW_FROM_SOURCE_BUILD_TYPE;
});
}
};
( run in 0.942 second using v1.01-cache-2.11-cpan-796a6f069b2 )